@levr-one/auth 1.0.0
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/README.md +37 -0
- package/dist/config-watcher.d.ts +65 -0
- package/dist/config-watcher.d.ts.map +1 -0
- package/dist/config-watcher.js +140 -0
- package/dist/config-watcher.js.map +1 -0
- package/dist/config-watcher.test.d.ts +2 -0
- package/dist/config-watcher.test.d.ts.map +1 -0
- package/dist/config-watcher.test.js +116 -0
- package/dist/config-watcher.test.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +234 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +221 -0
- package/dist/config.test.js.map +1 -0
- package/dist/file-lock.d.ts +41 -0
- package/dist/file-lock.d.ts.map +1 -0
- package/dist/file-lock.js +110 -0
- package/dist/file-lock.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth-client.d.ts +156 -0
- package/dist/oauth-client.d.ts.map +1 -0
- package/dist/oauth-client.js +572 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/oauth-client.test.d.ts +2 -0
- package/dist/oauth-client.test.d.ts.map +1 -0
- package/dist/oauth-client.test.js +290 -0
- package/dist/oauth-client.test.js.map +1 -0
- package/dist/test-utils.d.ts +10 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +45 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/token-store.d.ts +78 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +299 -0
- package/dist/token-store.js.map +1 -0
- package/dist/token-store.test.d.ts +2 -0
- package/dist/token-store.test.d.ts.map +1 -0
- package/dist/token-store.test.js +239 -0
- package/dist/token-store.test.js.map +1 -0
- package/dist/token-watcher.d.ts +82 -0
- package/dist/token-watcher.d.ts.map +1 -0
- package/dist/token-watcher.js +185 -0
- package/dist/token-watcher.js.map +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace-store.d.ts +60 -0
- package/dist/workspace-store.d.ts.map +1 -0
- package/dist/workspace-store.js +147 -0
- package/dist/workspace-store.js.map +1 -0
- package/dist/workspace-store.test.d.ts +2 -0
- package/dist/workspace-store.test.d.ts.map +1 -0
- package/dist/workspace-store.test.js +169 -0
- package/dist/workspace-store.test.js.map +1 -0
- package/dist/workspaces.d.ts +60 -0
- package/dist/workspaces.d.ts.map +1 -0
- package/dist/workspaces.js +80 -0
- package/dist/workspaces.js.map +1 -0
- package/dist/workspaces.test.d.ts +2 -0
- package/dist/workspaces.test.d.ts.map +1 -0
- package/dist/workspaces.test.js +111 -0
- package/dist/workspaces.test.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure token storage for OAuth tokens
|
|
3
|
+
*
|
|
4
|
+
* Stores tokens in ~/.tq/oauth-tokens.json as a map keyed by backend URL:
|
|
5
|
+
* {
|
|
6
|
+
* "https://api.levr.now": { accessToken, refreshToken, expiresAt, apiBaseUrl },
|
|
7
|
+
* "http://localhost:8080": { ... }
|
|
8
|
+
* }
|
|
9
|
+
*
|
|
10
|
+
* Backward-compatible: if the file contains the legacy single-token format
|
|
11
|
+
* (flat object with accessToken at the top level), it is migrated in-place
|
|
12
|
+
* to the new map format on first read.
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import * as os from 'os';
|
|
17
|
+
/**
|
|
18
|
+
* Token storage directory and file path
|
|
19
|
+
*/
|
|
20
|
+
const TQ_DIR = path.join(os.homedir(), '.tq');
|
|
21
|
+
const TOKEN_FILE = path.join(TQ_DIR, 'oauth-tokens.json');
|
|
22
|
+
/**
|
|
23
|
+
* Map of environment names to their backend URLs.
|
|
24
|
+
*/
|
|
25
|
+
// internal D15: unified levr.* origins. Legacy testquality.* URLs are
|
|
26
|
+
// retired during the DEC-11 7-day window; tokens minted under the
|
|
27
|
+
// legacy URLs are migrated by the legacy-key fallback in readTokenMap()
|
|
28
|
+
// (the apiBaseUrl on the StoredTokens itself was set when the token
|
|
29
|
+
// was originally issued, so old token files keep working transparently).
|
|
30
|
+
const ENV_URLS = {
|
|
31
|
+
local: 'http://localhost:8080',
|
|
32
|
+
staging: 'https://api.levr.now',
|
|
33
|
+
production: 'https://api.levr.one',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Resolve an environment name from a backend URL.
|
|
37
|
+
* Returns undefined if the URL doesn't match any known preset.
|
|
38
|
+
*/
|
|
39
|
+
export function resolveEnvFromUrl(url) {
|
|
40
|
+
const normalized = url.replace(/\/+$/, '');
|
|
41
|
+
for (const [env, envUrl] of Object.entries(ENV_URLS)) {
|
|
42
|
+
if (envUrl === normalized)
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a backend URL from an environment name.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveUrlFromEnv(env) {
|
|
51
|
+
return ENV_URLS[env];
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Internal: read/write the token map
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
/**
|
|
57
|
+
* Read the raw token map from disk.
|
|
58
|
+
* Handles migration from legacy single-token format.
|
|
59
|
+
*/
|
|
60
|
+
function readTokenMap() {
|
|
61
|
+
try {
|
|
62
|
+
if (!fs.existsSync(TOKEN_FILE))
|
|
63
|
+
return {};
|
|
64
|
+
const content = fs.readFileSync(TOKEN_FILE, 'utf-8');
|
|
65
|
+
const parsed = JSON.parse(content);
|
|
66
|
+
// Detect legacy single-token format (has accessToken at top level)
|
|
67
|
+
if ('accessToken' in parsed && 'refreshToken' in parsed) {
|
|
68
|
+
const legacy = parsed;
|
|
69
|
+
const key = legacy.apiBaseUrl ?? 'https://api.levr.now';
|
|
70
|
+
const map = { [key]: legacy };
|
|
71
|
+
// Migrate in place
|
|
72
|
+
writeTokenMap(map);
|
|
73
|
+
return map;
|
|
74
|
+
}
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error('[tq-oauth] Failed to load stored tokens:', error);
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Write the full token map to disk with atomic write.
|
|
84
|
+
*/
|
|
85
|
+
function writeTokenMap(map) {
|
|
86
|
+
if (!fs.existsSync(TQ_DIR)) {
|
|
87
|
+
fs.mkdirSync(TQ_DIR, { recursive: true, mode: 0o700 });
|
|
88
|
+
}
|
|
89
|
+
const tempPath = TOKEN_FILE + '.tmp';
|
|
90
|
+
fs.writeFileSync(tempPath, JSON.stringify(map, null, 2), {
|
|
91
|
+
mode: 0o600,
|
|
92
|
+
});
|
|
93
|
+
fs.renameSync(tempPath, TOKEN_FILE);
|
|
94
|
+
}
|
|
95
|
+
function isValidToken(t) {
|
|
96
|
+
const obj = t;
|
|
97
|
+
return !!(obj?.accessToken && obj?.refreshToken && obj?.expiresAt);
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Public API — drop-in compatible with the old single-token interface
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
/**
|
|
103
|
+
* Load the token for the current environment.
|
|
104
|
+
* Reads the config to determine which backend URL is active,
|
|
105
|
+
* then returns the matching entry from the token map.
|
|
106
|
+
*
|
|
107
|
+
* Falls back to first token in the map ONLY when no config exists
|
|
108
|
+
* (backward compat for first-time migration). When the user has
|
|
109
|
+
* explicitly set an environment, only that environment's token is returned.
|
|
110
|
+
*/
|
|
111
|
+
export function loadTokens() {
|
|
112
|
+
const map = readTokenMap();
|
|
113
|
+
const keys = Object.keys(map);
|
|
114
|
+
if (keys.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
// Try to match the current environment
|
|
117
|
+
const currentUrl = getCurrentApiUrl();
|
|
118
|
+
if (currentUrl) {
|
|
119
|
+
const entry = map[currentUrl];
|
|
120
|
+
if (entry && isValidToken(entry))
|
|
121
|
+
return entry;
|
|
122
|
+
// Config is set but no token for this env — don't fall back
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
// No config at all — fall back to first valid entry (backward compat)
|
|
126
|
+
for (const key of keys) {
|
|
127
|
+
if (isValidToken(map[key]))
|
|
128
|
+
return map[key];
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Load tokens for a specific environment by name.
|
|
134
|
+
*/
|
|
135
|
+
export function loadTokensForEnv(env) {
|
|
136
|
+
const map = readTokenMap();
|
|
137
|
+
const url = ENV_URLS[env];
|
|
138
|
+
const entry = map[url];
|
|
139
|
+
if (entry && isValidToken(entry))
|
|
140
|
+
return entry;
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Load tokens for an arbitrary backend URL (custom URL not in ENV_URLS).
|
|
145
|
+
*
|
|
146
|
+
* Used by the qinetic sync setup command, which needs to talk to a remote
|
|
147
|
+
* backend that may not be one of the named presets — the operator passes
|
|
148
|
+
* `--target <url>` and the CLI fetches the matching token entry by URL.
|
|
149
|
+
*/
|
|
150
|
+
export function loadTokensForUrl(url) {
|
|
151
|
+
const map = readTokenMap();
|
|
152
|
+
const normalized = url.replace(/\/$/, '');
|
|
153
|
+
// Try exact match first, then trailing-slash variant.
|
|
154
|
+
const entry = map[url] ?? map[normalized] ?? map[`${normalized}/`];
|
|
155
|
+
if (entry && isValidToken(entry))
|
|
156
|
+
return entry;
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Save tokens for the current environment.
|
|
161
|
+
* Upserts the entry keyed by the token's apiBaseUrl.
|
|
162
|
+
*/
|
|
163
|
+
export function saveTokens(tokens) {
|
|
164
|
+
try {
|
|
165
|
+
const map = readTokenMap();
|
|
166
|
+
const key = tokens.apiBaseUrl ?? getCurrentApiUrl() ?? ENV_URLS.staging;
|
|
167
|
+
map[key] = tokens;
|
|
168
|
+
writeTokenMap(map);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error('[tq-oauth] Failed to save tokens:', error);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Save tokens for a specific environment by name.
|
|
177
|
+
*/
|
|
178
|
+
export function saveTokensForEnv(env, tokens) {
|
|
179
|
+
const map = readTokenMap();
|
|
180
|
+
map[ENV_URLS[env]] = tokens;
|
|
181
|
+
writeTokenMap(map);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clear stored tokens for the current environment.
|
|
185
|
+
* Moves the entire file to .bak before removing the entry.
|
|
186
|
+
*/
|
|
187
|
+
export function clearTokens() {
|
|
188
|
+
try {
|
|
189
|
+
if (!fs.existsSync(TOKEN_FILE))
|
|
190
|
+
return;
|
|
191
|
+
// Backup entire file before modifying
|
|
192
|
+
const backupPath = TOKEN_FILE + '.bak';
|
|
193
|
+
try {
|
|
194
|
+
fs.copyFileSync(TOKEN_FILE, backupPath);
|
|
195
|
+
fs.chmodSync(backupPath, 0o600);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Backup failed — still proceed with clear
|
|
199
|
+
}
|
|
200
|
+
const map = readTokenMap();
|
|
201
|
+
const currentUrl = getCurrentApiUrl();
|
|
202
|
+
if (currentUrl && map[currentUrl]) {
|
|
203
|
+
delete map[currentUrl];
|
|
204
|
+
if (Object.keys(map).length === 0) {
|
|
205
|
+
fs.unlinkSync(TOKEN_FILE);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
writeTokenMap(map);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else if (!currentUrl) {
|
|
212
|
+
// No current URL known — clear entire file (legacy behavior)
|
|
213
|
+
fs.unlinkSync(TOKEN_FILE);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error('[tq-oauth] Failed to clear tokens:', error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Attempt to restore tokens from backup (.bak file)
|
|
222
|
+
* Returns true if backup was restored successfully.
|
|
223
|
+
*/
|
|
224
|
+
export function restoreTokensFromBackup() {
|
|
225
|
+
const backupPath = TOKEN_FILE + '.bak';
|
|
226
|
+
try {
|
|
227
|
+
if (fs.existsSync(backupPath) && !fs.existsSync(TOKEN_FILE)) {
|
|
228
|
+
const content = fs.readFileSync(backupPath, 'utf-8');
|
|
229
|
+
const parsed = JSON.parse(content);
|
|
230
|
+
// Handle both legacy and map format in backup
|
|
231
|
+
if ('accessToken' in parsed && parsed.refreshToken) {
|
|
232
|
+
const legacy = parsed;
|
|
233
|
+
const key = legacy.apiBaseUrl ?? ENV_URLS.staging;
|
|
234
|
+
writeTokenMap({ [key]: legacy });
|
|
235
|
+
console.error('[tq-oauth] Restored tokens from backup — refresh token recovered');
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
// Map format — write as-is if it has any entry with a refresh token
|
|
239
|
+
const map = parsed;
|
|
240
|
+
const hasRefresh = Object.values(map).some((t) => t.refreshToken);
|
|
241
|
+
if (hasRefresh) {
|
|
242
|
+
writeTokenMap(map);
|
|
243
|
+
console.error('[tq-oauth] Restored tokens from backup — refresh token recovered');
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
console.error('[tq-oauth] Failed to restore tokens from backup:', error);
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if token file exists and has any entries
|
|
255
|
+
*/
|
|
256
|
+
export function hasStoredTokens() {
|
|
257
|
+
if (!fs.existsSync(TOKEN_FILE))
|
|
258
|
+
return false;
|
|
259
|
+
const map = readTokenMap();
|
|
260
|
+
return Object.keys(map).length > 0;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get token file path (for debugging and watching)
|
|
264
|
+
*/
|
|
265
|
+
export function getTokenFilePath() {
|
|
266
|
+
return TOKEN_FILE;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get TQ directory path
|
|
270
|
+
*/
|
|
271
|
+
export function getTqDir() {
|
|
272
|
+
return TQ_DIR;
|
|
273
|
+
}
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
// Helpers
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
/**
|
|
278
|
+
* Read the current API URL from ~/.tq/config.json without importing
|
|
279
|
+
* config.ts (avoids circular dependency since config.ts imports us).
|
|
280
|
+
*/
|
|
281
|
+
function getCurrentApiUrl() {
|
|
282
|
+
try {
|
|
283
|
+
// Env var takes precedence
|
|
284
|
+
if (process.env.TQ_BACKEND_URL) {
|
|
285
|
+
return process.env.TQ_BACKEND_URL.replace(/\/+$/, '');
|
|
286
|
+
}
|
|
287
|
+
const configPath = path.join(TQ_DIR, 'config.json');
|
|
288
|
+
if (fs.existsSync(configPath)) {
|
|
289
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
290
|
+
if (raw.apiUrl)
|
|
291
|
+
return raw.apiUrl.replace(/\/+$/, '');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// Fall through
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.d.ts","sourceRoot":"","sources":["../src/token-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
// Mutable ref so the mock can read the current tmpDir
|
|
6
|
+
const state = vi.hoisted(() => ({ tmpDir: '' }));
|
|
7
|
+
// Mock os.homedir() so token-store's module-level TQ_DIR uses our temp dir
|
|
8
|
+
vi.mock('os', async (importOriginal) => {
|
|
9
|
+
const original = await importOriginal();
|
|
10
|
+
return { ...original, homedir: () => state.tmpDir };
|
|
11
|
+
});
|
|
12
|
+
// Re-import the module each test so module-level constants re-evaluate
|
|
13
|
+
let loadTokens;
|
|
14
|
+
let loadTokensForEnv;
|
|
15
|
+
let saveTokens;
|
|
16
|
+
let saveTokensForEnv;
|
|
17
|
+
let clearTokens;
|
|
18
|
+
let hasStoredTokens;
|
|
19
|
+
let getTqDir;
|
|
20
|
+
let getTokenFilePath;
|
|
21
|
+
let resolveEnvFromUrl;
|
|
22
|
+
let resolveUrlFromEnv;
|
|
23
|
+
const stagingTokens = {
|
|
24
|
+
accessToken: 'staging-access',
|
|
25
|
+
refreshToken: 'staging-refresh',
|
|
26
|
+
expiresAt: Date.now() + 3600000,
|
|
27
|
+
apiBaseUrl: 'https://api.levr.now',
|
|
28
|
+
};
|
|
29
|
+
const localTokens = {
|
|
30
|
+
accessToken: 'local-access',
|
|
31
|
+
refreshToken: 'local-refresh',
|
|
32
|
+
expiresAt: Date.now() + 3600000,
|
|
33
|
+
apiBaseUrl: 'http://localhost:8080',
|
|
34
|
+
};
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
state.tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tq-oauth-test-'));
|
|
37
|
+
// Reset module cache so TQ_DIR re-evaluates with the new homedir mock
|
|
38
|
+
vi.resetModules();
|
|
39
|
+
const mod = await import('./token-store.js');
|
|
40
|
+
loadTokens = mod.loadTokens;
|
|
41
|
+
loadTokensForEnv = mod.loadTokensForEnv;
|
|
42
|
+
saveTokens = mod.saveTokens;
|
|
43
|
+
saveTokensForEnv = mod.saveTokensForEnv;
|
|
44
|
+
clearTokens = mod.clearTokens;
|
|
45
|
+
hasStoredTokens = mod.hasStoredTokens;
|
|
46
|
+
getTqDir = mod.getTqDir;
|
|
47
|
+
getTokenFilePath = mod.getTokenFilePath;
|
|
48
|
+
resolveEnvFromUrl = mod.resolveEnvFromUrl;
|
|
49
|
+
resolveUrlFromEnv = mod.resolveUrlFromEnv;
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
delete process.env.TQ_BACKEND_URL;
|
|
53
|
+
fs.rmSync(state.tmpDir, { recursive: true, force: true });
|
|
54
|
+
});
|
|
55
|
+
describe('getTqDir', () => {
|
|
56
|
+
it('returns ~/.tq path based on homedir', () => {
|
|
57
|
+
expect(getTqDir()).toBe(path.join(state.tmpDir, '.tq'));
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('getTokenFilePath', () => {
|
|
61
|
+
it('returns path to oauth-tokens.json', () => {
|
|
62
|
+
expect(getTokenFilePath()).toBe(path.join(state.tmpDir, '.tq', 'oauth-tokens.json'));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('resolveEnvFromUrl', () => {
|
|
66
|
+
it('resolves local URL', () => {
|
|
67
|
+
expect(resolveEnvFromUrl('http://localhost:8080')).toBe('local');
|
|
68
|
+
});
|
|
69
|
+
it('resolves staging URL', () => {
|
|
70
|
+
expect(resolveEnvFromUrl('https://api.levr.now')).toBe('staging');
|
|
71
|
+
});
|
|
72
|
+
it('resolves production URL', () => {
|
|
73
|
+
expect(resolveEnvFromUrl('https://api.levr.one')).toBe('production');
|
|
74
|
+
});
|
|
75
|
+
it('strips trailing slashes', () => {
|
|
76
|
+
expect(resolveEnvFromUrl('http://localhost:8080/')).toBe('local');
|
|
77
|
+
});
|
|
78
|
+
it('returns undefined for unknown URLs', () => {
|
|
79
|
+
expect(resolveEnvFromUrl('https://custom.example.com')).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('resolveUrlFromEnv', () => {
|
|
83
|
+
it('returns correct URL for each env', () => {
|
|
84
|
+
expect(resolveUrlFromEnv('local')).toBe('http://localhost:8080');
|
|
85
|
+
expect(resolveUrlFromEnv('staging')).toBe('https://api.levr.now');
|
|
86
|
+
expect(resolveUrlFromEnv('production')).toBe('https://api.levr.one');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('saveTokens + loadTokens roundtrip', () => {
|
|
90
|
+
it('saves and loads tokens correctly', () => {
|
|
91
|
+
saveTokens(stagingTokens);
|
|
92
|
+
// loadTokens reads the entry matching the current env;
|
|
93
|
+
// without config.json it falls back to first entry
|
|
94
|
+
const loaded = loadTokens();
|
|
95
|
+
expect(loaded).toEqual(stagingTokens);
|
|
96
|
+
});
|
|
97
|
+
it('creates ~/.tq directory if missing', () => {
|
|
98
|
+
expect(fs.existsSync(path.join(state.tmpDir, '.tq'))).toBe(false);
|
|
99
|
+
saveTokens(stagingTokens);
|
|
100
|
+
expect(fs.existsSync(path.join(state.tmpDir, '.tq'))).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it('overwrites existing tokens for the same env', () => {
|
|
103
|
+
saveTokens(stagingTokens);
|
|
104
|
+
const updated = {
|
|
105
|
+
...stagingTokens,
|
|
106
|
+
accessToken: 'new-token',
|
|
107
|
+
};
|
|
108
|
+
saveTokens(updated);
|
|
109
|
+
const loaded = loadTokensForEnv('staging');
|
|
110
|
+
expect(loaded?.accessToken).toBe('new-token');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('token map: multiple environments', () => {
|
|
114
|
+
it('stores tokens for multiple environments in one file', () => {
|
|
115
|
+
saveTokens(stagingTokens);
|
|
116
|
+
saveTokens(localTokens);
|
|
117
|
+
expect(loadTokensForEnv('staging')?.accessToken).toBe('staging-access');
|
|
118
|
+
expect(loadTokensForEnv('local')?.accessToken).toBe('local-access');
|
|
119
|
+
expect(loadTokensForEnv('production')).toBeNull();
|
|
120
|
+
});
|
|
121
|
+
it('loadTokens returns entry matching current env via TQ_BACKEND_URL', () => {
|
|
122
|
+
saveTokens(stagingTokens);
|
|
123
|
+
saveTokens(localTokens);
|
|
124
|
+
process.env.TQ_BACKEND_URL = 'http://localhost:8080';
|
|
125
|
+
const loaded = loadTokens();
|
|
126
|
+
expect(loaded?.accessToken).toBe('local-access');
|
|
127
|
+
});
|
|
128
|
+
it('loadTokens returns entry matching config.json', () => {
|
|
129
|
+
saveTokens(stagingTokens);
|
|
130
|
+
saveTokens(localTokens);
|
|
131
|
+
// Write a config.json pointing to staging
|
|
132
|
+
const tqDir = path.join(state.tmpDir, '.tq');
|
|
133
|
+
fs.writeFileSync(path.join(tqDir, 'config.json'), JSON.stringify({ apiUrl: 'https://api.levr.now' }));
|
|
134
|
+
const loaded = loadTokens();
|
|
135
|
+
expect(loaded?.accessToken).toBe('staging-access');
|
|
136
|
+
});
|
|
137
|
+
it('loadTokens falls back to first entry when no config', () => {
|
|
138
|
+
saveTokens(stagingTokens);
|
|
139
|
+
const loaded = loadTokens();
|
|
140
|
+
expect(loaded?.accessToken).toBe('staging-access');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('backward compatibility: legacy single-token format', () => {
|
|
144
|
+
it('migrates legacy format on first read', () => {
|
|
145
|
+
const tqDir = path.join(state.tmpDir, '.tq');
|
|
146
|
+
fs.mkdirSync(tqDir, { recursive: true });
|
|
147
|
+
// Write legacy format (flat object)
|
|
148
|
+
fs.writeFileSync(path.join(tqDir, 'oauth-tokens.json'), JSON.stringify(stagingTokens));
|
|
149
|
+
const loaded = loadTokens();
|
|
150
|
+
expect(loaded?.accessToken).toBe('staging-access');
|
|
151
|
+
// File should now be in map format
|
|
152
|
+
const raw = JSON.parse(fs.readFileSync(path.join(tqDir, 'oauth-tokens.json'), 'utf-8'));
|
|
153
|
+
expect(raw['https://api.levr.now']).toBeDefined();
|
|
154
|
+
expect(raw['https://api.levr.now'].accessToken).toBe('staging-access');
|
|
155
|
+
});
|
|
156
|
+
it('migrates legacy format without apiBaseUrl using staging default', () => {
|
|
157
|
+
const tqDir = path.join(state.tmpDir, '.tq');
|
|
158
|
+
fs.mkdirSync(tqDir, { recursive: true });
|
|
159
|
+
const legacy = {
|
|
160
|
+
accessToken: 'a',
|
|
161
|
+
refreshToken: 'r',
|
|
162
|
+
expiresAt: 9999999999999,
|
|
163
|
+
};
|
|
164
|
+
fs.writeFileSync(path.join(tqDir, 'oauth-tokens.json'), JSON.stringify(legacy));
|
|
165
|
+
const loaded = loadTokens();
|
|
166
|
+
expect(loaded?.accessToken).toBe('a');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('loadTokens', () => {
|
|
170
|
+
it('returns null when no token file exists', () => {
|
|
171
|
+
expect(loadTokens()).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
it('returns null for malformed JSON', () => {
|
|
174
|
+
const tqDir = path.join(state.tmpDir, '.tq');
|
|
175
|
+
fs.mkdirSync(tqDir, { recursive: true });
|
|
176
|
+
fs.writeFileSync(path.join(tqDir, 'oauth-tokens.json'), 'not-json');
|
|
177
|
+
expect(loadTokens()).toBeNull();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
describe('loadTokensForEnv', () => {
|
|
181
|
+
it('returns null when no matching token exists', () => {
|
|
182
|
+
saveTokens(stagingTokens);
|
|
183
|
+
expect(loadTokensForEnv('local')).toBeNull();
|
|
184
|
+
});
|
|
185
|
+
it('returns the correct env token', () => {
|
|
186
|
+
saveTokens(stagingTokens);
|
|
187
|
+
saveTokens(localTokens);
|
|
188
|
+
expect(loadTokensForEnv('staging')?.accessToken).toBe('staging-access');
|
|
189
|
+
expect(loadTokensForEnv('local')?.accessToken).toBe('local-access');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
describe('clearTokens', () => {
|
|
193
|
+
it('removes the token for the current environment', () => {
|
|
194
|
+
saveTokens(stagingTokens);
|
|
195
|
+
saveTokens(localTokens);
|
|
196
|
+
// Clear staging (current env via TQ_BACKEND_URL)
|
|
197
|
+
process.env.TQ_BACKEND_URL = 'https://api.levr.now';
|
|
198
|
+
clearTokens();
|
|
199
|
+
expect(loadTokensForEnv('staging')).toBeNull();
|
|
200
|
+
expect(loadTokensForEnv('local')?.accessToken).toBe('local-access');
|
|
201
|
+
});
|
|
202
|
+
it('removes entire file when last token cleared', () => {
|
|
203
|
+
saveTokens(stagingTokens);
|
|
204
|
+
process.env.TQ_BACKEND_URL = 'https://api.levr.now';
|
|
205
|
+
clearTokens();
|
|
206
|
+
expect(hasStoredTokens()).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
it('does not throw when no token file exists', () => {
|
|
209
|
+
expect(() => clearTokens()).not.toThrow();
|
|
210
|
+
});
|
|
211
|
+
it('creates a backup before clearing', () => {
|
|
212
|
+
saveTokens(stagingTokens);
|
|
213
|
+
process.env.TQ_BACKEND_URL = 'https://api.levr.now';
|
|
214
|
+
clearTokens();
|
|
215
|
+
const backupPath = getTokenFilePath() + '.bak';
|
|
216
|
+
expect(fs.existsSync(backupPath)).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
describe('hasStoredTokens', () => {
|
|
220
|
+
it('returns false when no file exists', () => {
|
|
221
|
+
expect(hasStoredTokens()).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
it('returns true after saving tokens', () => {
|
|
224
|
+
saveTokens(stagingTokens);
|
|
225
|
+
expect(hasStoredTokens()).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe('saveTokensForEnv', () => {
|
|
229
|
+
it('saves and retrieves by env name', () => {
|
|
230
|
+
saveTokensForEnv('production', {
|
|
231
|
+
...stagingTokens,
|
|
232
|
+
accessToken: 'prod-access',
|
|
233
|
+
apiBaseUrl: 'https://api.levr.one',
|
|
234
|
+
});
|
|
235
|
+
const loaded = loadTokensForEnv('production');
|
|
236
|
+
expect(loaded?.accessToken).toBe('prod-access');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
//# sourceMappingURL=token-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.js","sourceRoot":"","sources":["../src/token-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TokenWatcher - Self-Healing Token Management
|
|
3
|
+
*
|
|
4
|
+
* Watches the token file for changes and notifies subscribers.
|
|
5
|
+
* This enables MCP servers to auto-recover when:
|
|
6
|
+
* - Tokens appear after startup (user runs authorize)
|
|
7
|
+
* - Tokens are refreshed externally
|
|
8
|
+
* - Token file is fixed after corruption
|
|
9
|
+
*/
|
|
10
|
+
import type { StoredTokens, TokenChangeCallback, AuthStatus } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Options for TokenWatcher
|
|
13
|
+
*/
|
|
14
|
+
export interface TokenWatcherOptions {
|
|
15
|
+
/** Polling interval in milliseconds (backup if fs.watch misses events) */
|
|
16
|
+
pollInterval?: number;
|
|
17
|
+
/** Minutes before expiration to consider token as expiring soon */
|
|
18
|
+
expirationWarningMinutes?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* TokenWatcher for self-healing token management
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const watcher = new TokenWatcher();
|
|
26
|
+
* watcher.onTokenChange((tokens) => {
|
|
27
|
+
* if (tokens) {
|
|
28
|
+
* console.log('Tokens available:', tokens.expiresAt > Date.now() ? 'valid' : 'expired');
|
|
29
|
+
* } else {
|
|
30
|
+
* console.log('No tokens available');
|
|
31
|
+
* }
|
|
32
|
+
* });
|
|
33
|
+
* watcher.start();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class TokenWatcher {
|
|
37
|
+
private watcher;
|
|
38
|
+
private pollInterval;
|
|
39
|
+
private lastTokens;
|
|
40
|
+
private lastTokensJson;
|
|
41
|
+
private callbacks;
|
|
42
|
+
private options;
|
|
43
|
+
private isRunning;
|
|
44
|
+
constructor(options?: TokenWatcherOptions);
|
|
45
|
+
/**
|
|
46
|
+
* Start watching for token changes
|
|
47
|
+
*/
|
|
48
|
+
start(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Stop watching for token changes
|
|
51
|
+
*/
|
|
52
|
+
stop(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Register a callback for token changes
|
|
55
|
+
*/
|
|
56
|
+
onTokenChange(callback: TokenChangeCallback): void;
|
|
57
|
+
/**
|
|
58
|
+
* Remove a token change callback
|
|
59
|
+
*/
|
|
60
|
+
offTokenChange(callback: TokenChangeCallback): void;
|
|
61
|
+
/**
|
|
62
|
+
* Get current tokens (synchronous)
|
|
63
|
+
*/
|
|
64
|
+
getCurrentTokens(): StoredTokens | null;
|
|
65
|
+
/**
|
|
66
|
+
* Get current auth status based on tokens
|
|
67
|
+
*/
|
|
68
|
+
getAuthStatus(): AuthStatus;
|
|
69
|
+
/**
|
|
70
|
+
* Check if tokens are valid (not expired)
|
|
71
|
+
*/
|
|
72
|
+
isTokenValid(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Force a token check (useful after manual refresh)
|
|
75
|
+
*/
|
|
76
|
+
forceCheck(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Internal: Check tokens and notify if changed
|
|
79
|
+
*/
|
|
80
|
+
private checkTokens;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=token-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-watcher.d.ts","sourceRoot":"","sources":["../src/token-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAOD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,SAAS,CAAkB;gBAEvB,OAAO,CAAC,EAAE,mBAAmB;IAIzC;;OAEG;IACH,KAAK,IAAI,IAAI;IA+Cb;;OAEG;IACH,IAAI,IAAI,IAAI;IAcZ;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IASlD;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAInD;;OAEG;IACH,gBAAgB,IAAI,YAAY,GAAG,IAAI;IAIvC;;OAEG;IACH,aAAa,IAAI,UAAU;IAoB3B;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,OAAO,CAAC,WAAW;CA2BpB"}
|