huntr-cli 1.0.9
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/.env.example +7 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- package/.github/labels.json +92 -0
- package/.github/pull_request_template.md +64 -0
- package/.github/workflows/ci.yml +87 -0
- package/.github/workflows/labels.yml +27 -0
- package/.github/workflows/manual-publish.yml +105 -0
- package/.github/workflows/publish.yml +57 -0
- package/.github/workflows/release.yml +124 -0
- package/.github/workflows/security-audit.yml +44 -0
- package/.husky/pre-commit +12 -0
- package/.husky/pre-push +27 -0
- package/.lintstagedrc.json +3 -0
- package/AGENTS.md +449 -0
- package/CHANGELOG.md +38 -0
- package/CHANGES.md +259 -0
- package/LICENSE +15 -0
- package/PUBLISHING.md +191 -0
- package/README.md +385 -0
- package/ROADMAP.md +158 -0
- package/SETUP-COMPLETE.md +446 -0
- package/WORKFLOW-SUMMARY.md +368 -0
- package/completions/_huntr +168 -0
- package/completions/huntr.1 +266 -0
- package/completions/huntr.bash +91 -0
- package/dist/api/client.d.ts +14 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +74 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/personal/activities.d.ts +20 -0
- package/dist/api/personal/activities.d.ts.map +1 -0
- package/dist/api/personal/activities.js +50 -0
- package/dist/api/personal/activities.js.map +1 -0
- package/dist/api/personal/boards.d.ts +9 -0
- package/dist/api/personal/boards.d.ts.map +1 -0
- package/dist/api/personal/boards.js +16 -0
- package/dist/api/personal/boards.js.map +1 -0
- package/dist/api/personal/index.d.ts +17 -0
- package/dist/api/personal/index.d.ts.map +1 -0
- package/dist/api/personal/index.js +37 -0
- package/dist/api/personal/index.js.map +1 -0
- package/dist/api/personal/jobs.d.ts +13 -0
- package/dist/api/personal/jobs.d.ts.map +1 -0
- package/dist/api/personal/jobs.js +31 -0
- package/dist/api/personal/jobs.js.map +1 -0
- package/dist/api/personal/user.d.ts +8 -0
- package/dist/api/personal/user.d.ts.map +1 -0
- package/dist/api/personal/user.js +13 -0
- package/dist/api/personal/user.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +501 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/capture-session.d.ts +10 -0
- package/dist/commands/capture-session.d.ts.map +1 -0
- package/dist/commands/capture-session.js +478 -0
- package/dist/commands/capture-session.js.map +1 -0
- package/dist/config/clerk-session-manager.d.ts +44 -0
- package/dist/config/clerk-session-manager.d.ts.map +1 -0
- package/dist/config/clerk-session-manager.js +232 -0
- package/dist/config/clerk-session-manager.js.map +1 -0
- package/dist/config/config-manager.d.ts +15 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +51 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/keychain-manager.d.ts +6 -0
- package/dist/config/keychain-manager.d.ts.map +1 -0
- package/dist/config/keychain-manager.js +37 -0
- package/dist/config/keychain-manager.js.map +1 -0
- package/dist/config/token-capture.d.ts +11 -0
- package/dist/config/token-capture.d.ts.map +1 -0
- package/dist/config/token-capture.js +252 -0
- package/dist/config/token-capture.js.map +1 -0
- package/dist/config/token-manager.d.ts +38 -0
- package/dist/config/token-manager.d.ts.map +1 -0
- package/dist/config/token-manager.js +153 -0
- package/dist/config/token-manager.js.map +1 -0
- package/dist/lib/list-options.d.ts +69 -0
- package/dist/lib/list-options.d.ts.map +1 -0
- package/dist/lib/list-options.js +299 -0
- package/dist/lib/list-options.js.map +1 -0
- package/dist/types/personal.d.ts +113 -0
- package/dist/types/personal.d.ts.map +1 -0
- package/dist/types/personal.js +4 -0
- package/dist/types/personal.js.map +1 -0
- package/docs/AUTOMATIC-PUBLISHING.md +520 -0
- package/docs/CHANGELOG-AUTOMATION.md +418 -0
- package/docs/CI-CD-SETUP.md +582 -0
- package/docs/DEV-SETUP.md +512 -0
- package/docs/ENHANCEMENT-PLAN.md +204 -0
- package/docs/ENTITY-TYPES.md +462 -0
- package/docs/GITHUB-ACTIONS-GUIDE.md +367 -0
- package/docs/NPM-PUBLISHING.md +324 -0
- package/docs/OUTPUT-EXAMPLES.md +414 -0
- package/docs/OUTPUT-FORMATS.md +299 -0
- package/docs/TESTING.md +216 -0
- package/eslint.config.js +68 -0
- package/package.json +64 -0
- package/src/api/client.ts +88 -0
- package/src/api/personal/activities.ts +66 -0
- package/src/api/personal/boards.ts +14 -0
- package/src/api/personal/index.ts +25 -0
- package/src/api/personal/jobs.ts +33 -0
- package/src/api/personal/user.ts +10 -0
- package/src/cli.ts +487 -0
- package/src/commands/capture-session.ts +582 -0
- package/src/config/clerk-session-manager.ts +263 -0
- package/src/config/config-manager.ts +56 -0
- package/src/config/keychain-manager.ts +30 -0
- package/src/config/token-capture.ts +233 -0
- package/src/config/token-manager.ts +139 -0
- package/src/lib/list-options.ts +370 -0
- package/src/types/personal.ts +114 -0
- package/tests/example.test.ts +130 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ClerkSessionManager = void 0;
|
|
7
|
+
const keytar_1 = __importDefault(require("keytar"));
|
|
8
|
+
const https_1 = __importDefault(require("https"));
|
|
9
|
+
const SERVICE_NAME = 'huntr-cli';
|
|
10
|
+
const ACCOUNT_SESSION_COOKIE = 'clerk-session-cookie'; // __session value
|
|
11
|
+
const ACCOUNT_SESSION_ID = 'clerk-session-id'; // sess_...
|
|
12
|
+
const ACCOUNT_CLIENT_UAT = 'clerk-client-uat'; // __client_uat value (optional)
|
|
13
|
+
const ACCOUNT_EXTRA_COOKIES = 'clerk-extra-cookies'; // JSON map of clerk-domain cookies
|
|
14
|
+
const CLERK_HOST = 'clerk.huntr.co';
|
|
15
|
+
/**
|
|
16
|
+
* Manages Clerk session-based JWT refresh for the Huntr CLI.
|
|
17
|
+
*
|
|
18
|
+
* Modern Clerk architecture (v5+):
|
|
19
|
+
* - __session cookie : stores the short-lived session JWT (60s exp).
|
|
20
|
+
* The cookie itself has a long expiry (1 year);
|
|
21
|
+
* Clerk JS updates its *value* via FAPI silently.
|
|
22
|
+
* - __client_uat : Unix timestamp of last client update — not a credential.
|
|
23
|
+
*
|
|
24
|
+
* To refresh from outside a browser, we POST the __session value to Clerk's
|
|
25
|
+
* FAPI tokens endpoint. Clerk validates the session and returns a fresh JWT.
|
|
26
|
+
*
|
|
27
|
+
* Endpoint:
|
|
28
|
+
* POST https://clerk.huntr.co/v1/client/sessions/{sessionId}/tokens
|
|
29
|
+
* Cookie: __session=<value>
|
|
30
|
+
* Response: { jwt: "ey..." }
|
|
31
|
+
*
|
|
32
|
+
* The __session value must be re-extracted from the browser periodically
|
|
33
|
+
* (roughly every few weeks, when Clerk rotates the underlying session).
|
|
34
|
+
* For daily use it persists fine.
|
|
35
|
+
*/
|
|
36
|
+
class ClerkSessionManager {
|
|
37
|
+
// ── storage ──────────────────────────────────────────────────────────────
|
|
38
|
+
async saveSession(sessionCookieValue, sessionId, clientUat, extraCookies) {
|
|
39
|
+
await keytar_1.default.setPassword(SERVICE_NAME, ACCOUNT_SESSION_COOKIE, sessionCookieValue);
|
|
40
|
+
await keytar_1.default.setPassword(SERVICE_NAME, ACCOUNT_SESSION_ID, sessionId);
|
|
41
|
+
if (clientUat) {
|
|
42
|
+
await keytar_1.default.setPassword(SERVICE_NAME, ACCOUNT_CLIENT_UAT, clientUat);
|
|
43
|
+
}
|
|
44
|
+
if (extraCookies && Object.keys(extraCookies).length > 0) {
|
|
45
|
+
await keytar_1.default.setPassword(SERVICE_NAME, ACCOUNT_EXTRA_COOKIES, JSON.stringify(extraCookies));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async getSessionCookie() {
|
|
49
|
+
return keytar_1.default.getPassword(SERVICE_NAME, ACCOUNT_SESSION_COOKIE);
|
|
50
|
+
}
|
|
51
|
+
async getSessionId() {
|
|
52
|
+
return keytar_1.default.getPassword(SERVICE_NAME, ACCOUNT_SESSION_ID);
|
|
53
|
+
}
|
|
54
|
+
async getClientUat() {
|
|
55
|
+
return keytar_1.default.getPassword(SERVICE_NAME, ACCOUNT_CLIENT_UAT);
|
|
56
|
+
}
|
|
57
|
+
async getExtraCookies() {
|
|
58
|
+
const raw = await keytar_1.default.getPassword(SERVICE_NAME, ACCOUNT_EXTRA_COOKIES);
|
|
59
|
+
if (!raw)
|
|
60
|
+
return {};
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(raw);
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
65
|
+
if (typeof v === 'string' && v.length > 0)
|
|
66
|
+
out[k] = v;
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async clearSession() {
|
|
75
|
+
await keytar_1.default.deletePassword(SERVICE_NAME, ACCOUNT_SESSION_COOKIE);
|
|
76
|
+
await keytar_1.default.deletePassword(SERVICE_NAME, ACCOUNT_SESSION_ID);
|
|
77
|
+
await keytar_1.default.deletePassword(SERVICE_NAME, ACCOUNT_CLIENT_UAT);
|
|
78
|
+
await keytar_1.default.deletePassword(SERVICE_NAME, ACCOUNT_EXTRA_COOKIES);
|
|
79
|
+
}
|
|
80
|
+
async hasSession() {
|
|
81
|
+
const cookie = await this.getSessionCookie();
|
|
82
|
+
const sessionId = await this.getSessionId();
|
|
83
|
+
return !!(cookie && sessionId);
|
|
84
|
+
}
|
|
85
|
+
// ── token refresh ─────────────────────────────────────────────────────────
|
|
86
|
+
/**
|
|
87
|
+
* Returns a fresh Clerk JWT by POSTing to the Clerk FAPI tokens endpoint
|
|
88
|
+
* using the stored __session cookie value.
|
|
89
|
+
*/
|
|
90
|
+
async getFreshToken() {
|
|
91
|
+
const sessionCookie = await this.getSessionCookie();
|
|
92
|
+
const sessionId = await this.getSessionId();
|
|
93
|
+
const clientUat = await this.getClientUat();
|
|
94
|
+
const extraCookies = await this.getExtraCookies();
|
|
95
|
+
if (!sessionCookie || !sessionId) {
|
|
96
|
+
throw new Error('No Clerk session stored. Run:\n' +
|
|
97
|
+
' huntr config set-session <__session-cookie-value>\n' +
|
|
98
|
+
'See "huntr config set-session --help" for instructions.');
|
|
99
|
+
}
|
|
100
|
+
return this.fetchToken(sessionCookie, sessionId, clientUat, extraCookies);
|
|
101
|
+
}
|
|
102
|
+
async refreshFromProvidedSession(sessionCookieValue, sessionId, clientUat, extraCookies) {
|
|
103
|
+
return this.fetchToken(sessionCookieValue, sessionId, clientUat, extraCookies);
|
|
104
|
+
}
|
|
105
|
+
fetchToken(sessionCookieValue, sessionId, clientUat, extraCookies = {}) {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
// Match the exact request Clerk JS makes (version pinned to what huntr.co loads)
|
|
108
|
+
const path = `/v1/client/sessions/${sessionId}/tokens?_clerk_js_version=4.73.14`;
|
|
109
|
+
// Strip any "__session=" prefix if user pasted it with the name
|
|
110
|
+
const raw = sessionCookieValue.startsWith('__session=')
|
|
111
|
+
? sessionCookieValue.slice('__session='.length)
|
|
112
|
+
: sessionCookieValue;
|
|
113
|
+
// Send all cookies Clerk expects, matching browser credentials:include behaviour
|
|
114
|
+
const uat = clientUat && clientUat.trim() ? clientUat.trim() : '1';
|
|
115
|
+
const cookieParts = [`__session=${raw}`, `__client_uat=${uat}`];
|
|
116
|
+
for (const [name, value] of Object.entries(extraCookies)) {
|
|
117
|
+
if (!value || name === '__session' || name === '__client_uat')
|
|
118
|
+
continue;
|
|
119
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(name))
|
|
120
|
+
continue;
|
|
121
|
+
cookieParts.push(`${name}=${value}`);
|
|
122
|
+
}
|
|
123
|
+
const cookieHeader = cookieParts.join('; ');
|
|
124
|
+
const options = {
|
|
125
|
+
hostname: CLERK_HOST,
|
|
126
|
+
path,
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: {
|
|
129
|
+
'Cookie': cookieHeader,
|
|
130
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
131
|
+
'Content-Length': '0',
|
|
132
|
+
'Accept': 'application/json',
|
|
133
|
+
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
|
|
134
|
+
'Origin': 'https://huntr.co',
|
|
135
|
+
'Referer': 'https://huntr.co/',
|
|
136
|
+
'sec-fetch-site': 'same-site',
|
|
137
|
+
'sec-fetch-mode': 'cors',
|
|
138
|
+
'sec-fetch-dest': 'empty',
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const req = https_1.default.request(options, (res) => {
|
|
142
|
+
let body = '';
|
|
143
|
+
res.on('data', (chunk) => { body += chunk.toString(); });
|
|
144
|
+
res.on('end', async () => {
|
|
145
|
+
if (res.statusCode === 200 || res.statusCode === 201) {
|
|
146
|
+
try {
|
|
147
|
+
const data = JSON.parse(body);
|
|
148
|
+
// Clerk FAPI response: { object: 'token', jwt: '...' }
|
|
149
|
+
const token = data.jwt ?? data.token ?? data.object?.jwt;
|
|
150
|
+
if (!token) {
|
|
151
|
+
reject(new Error(`Unexpected Clerk response: ${body.substring(0, 300)}`));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Clerk may rotate __session on refresh via Set-Cookie.
|
|
155
|
+
// Persisting it prevents "works once, fails later" behavior.
|
|
156
|
+
await this.persistRotatedSessionCookie(res.headers['set-cookie'], sessionId, raw, uat, extraCookies);
|
|
157
|
+
resolve(token);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
reject(new Error(`Failed to parse Clerk response: ${body.substring(0, 300)}`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (res.statusCode === 401 || res.statusCode === 403) {
|
|
165
|
+
reject(new Error(`Clerk session expired or invalid (HTTP ${res.statusCode}).\n` +
|
|
166
|
+
'Re-extract your __session cookie from the browser:\n' +
|
|
167
|
+
' DevTools → Application → Cookies → https://huntr.co → __session → Value\n' +
|
|
168
|
+
'Then run: huntr config set-session <new-value>'));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
reject(new Error(`Clerk token refresh failed: HTTP ${res.statusCode}\n${body.substring(0, 300)}`));
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
req.on('error', (err) => reject(new Error(`Network error refreshing token: ${err.message}`)));
|
|
176
|
+
req.end();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async persistRotatedSessionCookie(setCookie, sessionId, currentSessionCookie, clientUat, extraCookies = {}) {
|
|
180
|
+
if (!setCookie)
|
|
181
|
+
return;
|
|
182
|
+
const cookies = Array.isArray(setCookie) ? setCookie : [setCookie];
|
|
183
|
+
let sessionCookie = currentSessionCookie;
|
|
184
|
+
let rotatedUat = clientUat;
|
|
185
|
+
const rotatedExtras = { ...extraCookies };
|
|
186
|
+
for (const header of cookies) {
|
|
187
|
+
const firstPair = header.split(';', 1)[0] ?? '';
|
|
188
|
+
const idx = firstPair.indexOf('=');
|
|
189
|
+
if (idx <= 0)
|
|
190
|
+
continue;
|
|
191
|
+
const name = firstPair.slice(0, idx);
|
|
192
|
+
const value = firstPair.slice(idx + 1);
|
|
193
|
+
if (!value)
|
|
194
|
+
continue;
|
|
195
|
+
if (name === '__session') {
|
|
196
|
+
sessionCookie = value;
|
|
197
|
+
}
|
|
198
|
+
else if (name === '__client_uat') {
|
|
199
|
+
rotatedUat = value;
|
|
200
|
+
}
|
|
201
|
+
else if (/^[A-Za-z0-9_.-]+$/.test(name)) {
|
|
202
|
+
rotatedExtras[name] = value;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
await this.saveSession(sessionCookie, sessionId, rotatedUat, rotatedExtras);
|
|
206
|
+
}
|
|
207
|
+
// ── session-id extraction ─────────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Extracts the Clerk session ID (sess_...) from a __session JWT value.
|
|
210
|
+
* The __session cookie value IS a JWT; its payload contains `sid`.
|
|
211
|
+
*/
|
|
212
|
+
static extractSessionId(sessionJwt) {
|
|
213
|
+
try {
|
|
214
|
+
const raw = sessionJwt.startsWith('__session=')
|
|
215
|
+
? sessionJwt.slice('__session='.length)
|
|
216
|
+
: sessionJwt;
|
|
217
|
+
const parts = raw.split('.');
|
|
218
|
+
if (parts.length < 2)
|
|
219
|
+
return null;
|
|
220
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
|
|
221
|
+
const sid = payload.sid ?? payload.session_id;
|
|
222
|
+
if (typeof sid === 'string' && sid.startsWith('sess_'))
|
|
223
|
+
return sid;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// fall through
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.ClerkSessionManager = ClerkSessionManager;
|
|
232
|
+
//# sourceMappingURL=clerk-session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk-session-manager.js","sourceRoot":"","sources":["../../src/config/clerk-session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface HuntrConfig {
|
|
2
|
+
apiToken?: string;
|
|
3
|
+
}
|
|
4
|
+
export declare class ConfigManager {
|
|
5
|
+
private configDir;
|
|
6
|
+
private configPath;
|
|
7
|
+
constructor();
|
|
8
|
+
private ensureConfigDir;
|
|
9
|
+
getConfig(): HuntrConfig;
|
|
10
|
+
setConfig(config: HuntrConfig): void;
|
|
11
|
+
getToken(): string | undefined;
|
|
12
|
+
setToken(token: string): void;
|
|
13
|
+
clearToken(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=config-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;;IAO3B,OAAO,CAAC,eAAe;IAMvB,SAAS,IAAI,WAAW;IAYxB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKpC,QAAQ,IAAI,MAAM,GAAG,SAAS;IAI9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7B,UAAU,IAAI,IAAI;CAKnB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ConfigManager = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
class ConfigManager {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.configDir = path_1.default.join(os_1.default.homedir(), '.huntr');
|
|
13
|
+
this.configPath = path_1.default.join(this.configDir, 'config.json');
|
|
14
|
+
}
|
|
15
|
+
ensureConfigDir() {
|
|
16
|
+
if (!fs_1.default.existsSync(this.configDir)) {
|
|
17
|
+
fs_1.default.mkdirSync(this.configDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
getConfig() {
|
|
21
|
+
try {
|
|
22
|
+
if (fs_1.default.existsSync(this.configPath)) {
|
|
23
|
+
const content = fs_1.default.readFileSync(this.configPath, 'utf-8');
|
|
24
|
+
return JSON.parse(content);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
// Return empty config if file doesn't exist or is invalid
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
setConfig(config) {
|
|
33
|
+
this.ensureConfigDir();
|
|
34
|
+
fs_1.default.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
|
|
35
|
+
}
|
|
36
|
+
getToken() {
|
|
37
|
+
return this.getConfig().apiToken;
|
|
38
|
+
}
|
|
39
|
+
setToken(token) {
|
|
40
|
+
const config = this.getConfig();
|
|
41
|
+
config.apiToken = token;
|
|
42
|
+
this.setConfig(config);
|
|
43
|
+
}
|
|
44
|
+
clearToken() {
|
|
45
|
+
const config = this.getConfig();
|
|
46
|
+
delete config.apiToken;
|
|
47
|
+
this.setConfig(config);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ConfigManager = ConfigManager;
|
|
51
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAMpB,MAAa,aAAa;IAIxB;QACE,IAAI,CAAC,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC7D,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,YAAE,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,SAAS;QACP,IAAI,CAAC;YACH,IAAI,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;gBACnC,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0DAA0D;QAC5D,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,SAAS,CAAC,MAAmB;QAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,YAAE,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC;IACnC,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,UAAU;QACR,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,OAAO,MAAM,CAAC,QAAQ,CAAC;QACvB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;CACF;AA/CD,sCA+CC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain-manager.d.ts","sourceRoot":"","sources":["../../src/config/keychain-manager.ts"],"names":[],"mappings":"AAKA,qBAAa,eAAe;IACpB,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQlC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAOtC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.KeychainManager = void 0;
|
|
7
|
+
const keytar_1 = __importDefault(require("keytar"));
|
|
8
|
+
const SERVICE_NAME = 'huntr-cli';
|
|
9
|
+
const ACCOUNT_NAME = 'api-token';
|
|
10
|
+
class KeychainManager {
|
|
11
|
+
async getToken() {
|
|
12
|
+
try {
|
|
13
|
+
return await keytar_1.default.getPassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async setToken(token) {
|
|
20
|
+
try {
|
|
21
|
+
await keytar_1.default.setPassword(SERVICE_NAME, ACCOUNT_NAME, token);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error('Failed to save token to keychain');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async deleteToken() {
|
|
28
|
+
try {
|
|
29
|
+
return await keytar_1.default.deletePassword(SERVICE_NAME, ACCOUNT_NAME);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.KeychainManager = KeychainManager;
|
|
37
|
+
//# sourceMappingURL=keychain-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain-manager.js","sourceRoot":"","sources":["../../src/config/keychain-manager.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,MAAM,YAAY,GAAG,WAAW,CAAC;AACjC,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC,MAAa,eAAe;IAC1B,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,OAAO,MAAM,gBAAM,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa;QAC1B,IAAI,CAAC;YACH,MAAM,gBAAM,CAAC,WAAW,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,OAAO,MAAM,gBAAM,CAAC,cAAc,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAxBD,0CAwBC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TokenManager } from './token-manager';
|
|
2
|
+
export declare class TokenCaptureServer {
|
|
3
|
+
private server;
|
|
4
|
+
private tokenManager;
|
|
5
|
+
private htmlPath;
|
|
6
|
+
constructor(tokenManager: TokenManager);
|
|
7
|
+
start(port?: number): Promise<string>;
|
|
8
|
+
stop(): void;
|
|
9
|
+
private getHtmlPage;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=token-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-capture.d.ts","sourceRoot":"","sources":["../../src/config/token-capture.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAS;gBAEb,YAAY,EAAE,YAAY;IAKhC,KAAK,CAAC,IAAI,GAAE,MAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAiFlD,IAAI;IAOJ,OAAO,CAAC,WAAW;CAiIpB"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TokenCaptureServer = void 0;
|
|
37
|
+
const http = __importStar(require("http"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
class TokenCaptureServer {
|
|
41
|
+
constructor(tokenManager) {
|
|
42
|
+
this.server = null;
|
|
43
|
+
this.tokenManager = tokenManager;
|
|
44
|
+
this.htmlPath = path.join(os.tmpdir(), 'huntr-token-capture.html');
|
|
45
|
+
}
|
|
46
|
+
async start(port = 17432) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
this.server = http.createServer(async (req, res) => {
|
|
49
|
+
// Enable CORS for file:// protocol
|
|
50
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
51
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
52
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
53
|
+
if (req.method === 'OPTIONS') {
|
|
54
|
+
res.writeHead(200);
|
|
55
|
+
res.end();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Serve the HTML page
|
|
59
|
+
if (req.method === 'GET' && req.url === '/') {
|
|
60
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
61
|
+
res.end(this.getHtmlPage(port));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (req.method === 'POST' && req.url === '/token') {
|
|
65
|
+
let body = '';
|
|
66
|
+
req.on('data', chunk => {
|
|
67
|
+
body += chunk.toString();
|
|
68
|
+
});
|
|
69
|
+
req.on('end', async () => {
|
|
70
|
+
try {
|
|
71
|
+
const data = JSON.parse(body);
|
|
72
|
+
const token = data.token;
|
|
73
|
+
if (!token) {
|
|
74
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
75
|
+
res.end(JSON.stringify({ ok: false, error: 'No token provided' }));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Save token to config file
|
|
79
|
+
await this.tokenManager.saveToken(token, 'config');
|
|
80
|
+
const sessionId = `session_${Date.now()}`;
|
|
81
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
82
|
+
res.end(JSON.stringify({ ok: true, sessionId }));
|
|
83
|
+
console.log('\n✓ Token captured and saved!');
|
|
84
|
+
console.log('You can now use the CLI without --token flag.');
|
|
85
|
+
// Close server after successful capture
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
this.stop();
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}, 100);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
93
|
+
res.end(JSON.stringify({ ok: false, error: 'Internal server error' }));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
res.writeHead(404);
|
|
99
|
+
res.end('Not found');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this.server.on('error', (error) => {
|
|
103
|
+
if (error.code === 'EADDRINUSE') {
|
|
104
|
+
reject(new Error(`Port ${port} is already in use`));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
reject(error);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
this.server.listen(port, () => {
|
|
111
|
+
resolve(`http://127.0.0.1:${port}`);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
stop() {
|
|
116
|
+
if (this.server) {
|
|
117
|
+
this.server.close();
|
|
118
|
+
this.server = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
getHtmlPage(port) {
|
|
122
|
+
return `<!DOCTYPE html>
|
|
123
|
+
<html>
|
|
124
|
+
<head>
|
|
125
|
+
<meta charset="UTF-8">
|
|
126
|
+
<title>Huntr Token Capture</title>
|
|
127
|
+
<style>
|
|
128
|
+
body {
|
|
129
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
130
|
+
max-width: 600px;
|
|
131
|
+
margin: 50px auto;
|
|
132
|
+
padding: 20px;
|
|
133
|
+
background: #f5f5f5;
|
|
134
|
+
}
|
|
135
|
+
.container {
|
|
136
|
+
background: white;
|
|
137
|
+
padding: 30px;
|
|
138
|
+
border-radius: 8px;
|
|
139
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
140
|
+
}
|
|
141
|
+
h1 { color: #333; margin-top: 0; }
|
|
142
|
+
.step {
|
|
143
|
+
margin: 20px 0;
|
|
144
|
+
padding: 15px;
|
|
145
|
+
background: #f8f9fa;
|
|
146
|
+
border-left: 4px solid #007bff;
|
|
147
|
+
border-radius: 4px;
|
|
148
|
+
}
|
|
149
|
+
code {
|
|
150
|
+
background: #e9ecef;
|
|
151
|
+
padding: 2px 6px;
|
|
152
|
+
border-radius: 3px;
|
|
153
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
154
|
+
font-size: 14px;
|
|
155
|
+
}
|
|
156
|
+
.snippet {
|
|
157
|
+
background: #2d2d2d;
|
|
158
|
+
color: #f8f8f2;
|
|
159
|
+
padding: 15px;
|
|
160
|
+
border-radius: 4px;
|
|
161
|
+
margin: 10px 0;
|
|
162
|
+
overflow-x: auto;
|
|
163
|
+
font-family: 'Monaco', 'Courier New', monospace;
|
|
164
|
+
font-size: 13px;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
}
|
|
167
|
+
.snippet:hover { background: #3d3d3d; }
|
|
168
|
+
.success { color: #28a745; font-weight: bold; }
|
|
169
|
+
.error { color: #dc3545; }
|
|
170
|
+
button {
|
|
171
|
+
background: #007bff;
|
|
172
|
+
color: white;
|
|
173
|
+
border: none;
|
|
174
|
+
padding: 10px 20px;
|
|
175
|
+
border-radius: 4px;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
font-size: 14px;
|
|
178
|
+
}
|
|
179
|
+
button:hover { background: #0056b3; }
|
|
180
|
+
#status { margin-top: 20px; font-weight: bold; }
|
|
181
|
+
</style>
|
|
182
|
+
</head>
|
|
183
|
+
<body>
|
|
184
|
+
<div class="container">
|
|
185
|
+
<h1>🎯 Huntr Token Capture</h1>
|
|
186
|
+
|
|
187
|
+
<div class="step">
|
|
188
|
+
<strong>Step 1:</strong> Open <a href="https://huntr.co" target="_blank">huntr.co</a> and log in
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div class="step">
|
|
192
|
+
<strong>Step 2:</strong> Open DevTools Console (F12 or Cmd+Option+J)
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="step">
|
|
196
|
+
<strong>Step 3:</strong> Run this in the Huntr console:
|
|
197
|
+
<div class="snippet" onclick="copySnippet()" title="Click to copy">
|
|
198
|
+
(async()=>{var t=await window.Clerk.session.getToken({skipCache:true});window.opener?.postMessage({type:'HUNTR_TOKEN',token:t},'http://127.0.0.1:${port}');window.close();console.log('✓ Token sent!')})();
|
|
199
|
+
</div>
|
|
200
|
+
<small style="color: #666;">Click to copy, or click button below</small>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<button onclick="openHuntr()">Open Huntr & Capture Token</button>
|
|
204
|
+
|
|
205
|
+
<div id="status"></div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<script>
|
|
209
|
+
let huntrWindow = null;
|
|
210
|
+
|
|
211
|
+
function copySnippet() {
|
|
212
|
+
const text = \`(async()=>{var t=await window.Clerk.session.getToken({skipCache:true});window.opener?.postMessage({type:'HUNTR_TOKEN',token:t},'http://127.0.0.1:${port}');window.close();console.log('✓ Token sent!')})();\`;
|
|
213
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
214
|
+
document.getElementById('status').innerHTML = '<span class="success">✓ Copied! Now paste in Huntr Console</span>';
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function openHuntr() {
|
|
219
|
+
huntrWindow = window.open('https://huntr.co', 'huntr', 'width=800,height=600');
|
|
220
|
+
document.getElementById('status').innerHTML = '<span style="color: #666;">Waiting for token from Huntr window...</span>';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
window.addEventListener('message', async (event) => {
|
|
224
|
+
if (event.origin !== 'https://huntr.co') return;
|
|
225
|
+
if (event.data.type === 'HUNTR_TOKEN' && event.data.token) {
|
|
226
|
+
document.getElementById('status').innerHTML = '<span style="color: #666;">Sending token to CLI...</span>';
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const response = await fetch('http://127.0.0.1:${port}/token', {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: { 'Content-Type': 'application/json' },
|
|
232
|
+
body: JSON.stringify({ token: event.data.token })
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (response.ok) {
|
|
236
|
+
document.getElementById('status').innerHTML = '<span class="success">✓ Token captured! You can close this window.</span>';
|
|
237
|
+
setTimeout(() => window.close(), 2000);
|
|
238
|
+
} else {
|
|
239
|
+
document.getElementById('status').innerHTML = '<span class="error">Error saving token</span>';
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
document.getElementById('status').innerHTML = '<span class="error">Error: ' + error.message + '</span>';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
</script>
|
|
247
|
+
</body>
|
|
248
|
+
</html>`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
exports.TokenCaptureServer = TokenCaptureServer;
|
|
252
|
+
//# sourceMappingURL=token-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-capture.js","sourceRoot":"","sources":["../../src/config/token-capture.ts"],"names":[],"mappings":"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"}
|