@empirical-gauzza/cli 1.0.22 → 1.0.24
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 +63 -63
- package/bin/empirical.js +1 -1
- package/lib/constants.js +14 -1
- package/lib/doc.js +1 -1
- package/lib/env.js +49 -49
- package/lib/installers/detect.js +47 -47
- package/lib/installers/hooks.js +3 -3
- package/lib/installers/skill-variants/SKILL.both.md +17 -1
- package/lib/installers/skill-variants/SKILL.cli.md +17 -1
- package/lib/installers/skill-variants/SKILL.mcp.md +13 -2
- package/lib/memoryCommands.js +42 -3
- package/lib/oauthClient.js +222 -222
- package/lib/policyCommands.js +144 -144
- package/lib/workspaceContext.js +47 -47
- package/package.json +1 -1
package/lib/oauthClient.js
CHANGED
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const os = require('os');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { readJsonIfExists, writeFile, ensureDir } = require('./fsUtils');
|
|
7
|
-
const { getEnv } = require('./env');
|
|
8
|
-
const { runResolvedCommand } = require('./fsUtils');
|
|
9
|
-
|
|
10
|
-
function getLocalOAuthClientPath() {
|
|
11
|
-
return path.join(os.homedir(), '.empirical', 'oauth-client.json');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function readStoredOAuthClient() {
|
|
15
|
-
const stored = readJsonIfExists(getLocalOAuthClientPath());
|
|
16
|
-
if (!stored || typeof stored !== 'object') return null;
|
|
17
|
-
const clientId = String(stored.clientId || '').trim();
|
|
18
|
-
if (!clientId) return null;
|
|
19
|
-
return {
|
|
20
|
-
clientId,
|
|
21
|
-
clientSecret: String(stored.clientSecret || '')
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function writeStoredOAuthClient(clientId, clientSecret) {
|
|
26
|
-
const payload = {
|
|
27
|
-
clientId: String(clientId || '').trim(),
|
|
28
|
-
clientSecret: String(clientSecret || '').trim(),
|
|
29
|
-
updatedAt: new Date().toISOString()
|
|
30
|
-
};
|
|
31
|
-
writeFile(getLocalOAuthClientPath(), `${JSON.stringify(payload, null, 2)}\n`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function clearStoredOAuthClient() {
|
|
35
|
-
const storePath = getLocalOAuthClientPath();
|
|
36
|
-
if (fs.existsSync(storePath)) {
|
|
37
|
-
fs.unlinkSync(storePath);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function maskSecret(value) {
|
|
42
|
-
const raw = String(value || '');
|
|
43
|
-
if (!raw) return '(not set)';
|
|
44
|
-
if (raw.length <= 8) return '*'.repeat(raw.length);
|
|
45
|
-
return `${raw.slice(0, 4)}...${raw.slice(-4)}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function generateRandomString(length = 32) {
|
|
49
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
50
|
-
let result = '';
|
|
51
|
-
for (let i = 0; i < length; i += 1) {
|
|
52
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function base64url(buf) {
|
|
58
|
-
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function generatePKCE() {
|
|
62
|
-
const crypto = require('crypto');
|
|
63
|
-
const codeVerifier = generateRandomString(128);
|
|
64
|
-
const codeChallenge = base64url(crypto.createHash('sha256').update(codeVerifier).digest());
|
|
65
|
-
return { codeVerifier, codeChallenge };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function decodeJwtPayload(token) {
|
|
69
|
-
try {
|
|
70
|
-
const parts = String(token).split('.');
|
|
71
|
-
if (parts.length !== 3) return null;
|
|
72
|
-
return JSON.parse(Buffer.from(parts[1], 'base64url').toString());
|
|
73
|
-
} catch (_error) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function isJwtToken(token) {
|
|
79
|
-
return typeof token === 'string' && token.split('.').length === 3;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function isJwtExpired(token, skewSeconds = 30) {
|
|
83
|
-
const payload = decodeJwtPayload(token);
|
|
84
|
-
if (!payload || typeof payload.exp !== 'number') return true;
|
|
85
|
-
return payload.exp < Math.floor(Date.now() / 1000) + skewSeconds;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getHeadlessTokenPath() {
|
|
89
|
-
return path.resolve(getEnv('EMPIRICAL_TOKEN_OUTPUT', '.empirical/headless-token.json'));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getOAuthSessionPath() {
|
|
93
|
-
return path.join(os.homedir(), '.empirical', 'oauth-session.json');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function readOAuthSession() {
|
|
97
|
-
const session = readJsonIfExists(getOAuthSessionPath());
|
|
98
|
-
if (!session || typeof session !== 'object') return null;
|
|
99
|
-
return session;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function writeOAuthSession(session) {
|
|
103
|
-
if (!session?.accessToken) return;
|
|
104
|
-
writeFile(getOAuthSessionPath(), `${JSON.stringify(session, null, 2)}\n`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function clearOAuthSession() {
|
|
108
|
-
const sessionPath = getOAuthSessionPath();
|
|
109
|
-
if (fs.existsSync(sessionPath)) fs.unlinkSync(sessionPath);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function readHeadlessToken() {
|
|
113
|
-
const tokenPath = getHeadlessTokenPath();
|
|
114
|
-
if (!fs.existsSync(tokenPath)) return null;
|
|
115
|
-
try {
|
|
116
|
-
const parsed = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
|
|
117
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const normalized = { ...parsed };
|
|
122
|
-
if (!normalized.clientId && normalized.authClientId) {
|
|
123
|
-
normalized.clientId = normalized.authClientId;
|
|
124
|
-
}
|
|
125
|
-
if (!normalized.clientSecret && normalized.authClientSecret) {
|
|
126
|
-
normalized.clientSecret = normalized.authClientSecret;
|
|
127
|
-
}
|
|
128
|
-
if (!normalized.apiKey) {
|
|
129
|
-
normalized.apiKey = normalized.clientSecret || normalized.authClientSecret || null;
|
|
130
|
-
}
|
|
131
|
-
return normalized;
|
|
132
|
-
} catch (_error) {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function getAccessTokenExpiryMs(token) {
|
|
138
|
-
if (!token || !token.accessToken) return 0;
|
|
139
|
-
const obtainedAtMs = Date.parse(String(token.obtainedAt || ''));
|
|
140
|
-
const expiresInSeconds = Number(token.expiresIn || 0);
|
|
141
|
-
if (!Number.isFinite(obtainedAtMs) || !Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0) {
|
|
142
|
-
return 0;
|
|
143
|
-
}
|
|
144
|
-
return obtainedAtMs + (expiresInSeconds * 1000);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function hasFreshAccessToken(token, skewSeconds = 120) {
|
|
148
|
-
const expiryMs = getAccessTokenExpiryMs(token);
|
|
149
|
-
if (!expiryMs) return false;
|
|
150
|
-
return expiryMs > (Date.now() + (skewSeconds * 1000));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function persistAccessToken(accessToken) {
|
|
154
|
-
if (!accessToken) return;
|
|
155
|
-
process.env.EMPIRICAL_ACCESS_TOKEN = accessToken;
|
|
156
|
-
if (process.platform !== 'win32') {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
// `setx` writes permanently into the user's Windows environment, surviving
|
|
160
|
-
// every future shell/process. Only do that for a genuinely permanent
|
|
161
|
-
// credential (a plain apiKey) — a short-lived session JWT written here
|
|
162
|
-
// would silently outlive its own expiry and shadow a valid apiKey in the
|
|
163
|
-
// token file for good, forcing a manual env-var reset roughly every hour.
|
|
164
|
-
if (isJwtToken(accessToken)) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const escaped = String(accessToken).replace(/\"/g, '""');
|
|
168
|
-
runResolvedCommand('setx', ['EMPIRICAL_ACCESS_TOKEN', escaped], { stdio: 'ignore' });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function toEnvSafe(value) {
|
|
172
|
-
return String(value ?? '')
|
|
173
|
-
.replace(/\\/g, '\\\\')
|
|
174
|
-
.replace(/\n/g, '\\n')
|
|
175
|
-
.replace(/\r/g, '\\r')
|
|
176
|
-
.replace(/"/g, '\\"');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function writeHeadlessEnvFile(result, outputPath) {
|
|
180
|
-
ensureDir(path.dirname(path.resolve(outputPath)));
|
|
181
|
-
// Prefer the API key as the access token — it never expires and works as a
|
|
182
|
-
// permanent Bearer credential with the MCP endpoint.
|
|
183
|
-
const permanentToken = result.apiKey || result.accessToken;
|
|
184
|
-
const lines = [
|
|
185
|
-
'# Generated by empirical cli',
|
|
186
|
-
`EMPIRICAL_BASE_URL="${toEnvSafe(result.baseUrl)}"`,
|
|
187
|
-
`EMPIRICAL_ACCESS_TOKEN="${toEnvSafe(permanentToken)}"`,
|
|
188
|
-
`EMPIRICAL_API_KEY="${toEnvSafe(result.apiKey || '')}"`,
|
|
189
|
-
`EMPIRICAL_REFRESH_TOKEN="${toEnvSafe(result.refreshToken || '')}"`,
|
|
190
|
-
`EMPIRICAL_CLIENT_ID="${toEnvSafe(result.clientId || '')}"`,
|
|
191
|
-
`EMPIRICAL_CLIENT_SECRET="${toEnvSafe(result.clientSecret || '')}"`,
|
|
192
|
-
`EMPIRICAL_TOKEN_TYPE="${toEnvSafe(result.tokenType || 'Bearer')}"`,
|
|
193
|
-
`EMPIRICAL_TOKEN_OBTAINED_AT="${toEnvSafe(result.obtainedAt || '')}"`,
|
|
194
|
-
`EMPIRICAL_TOKEN_EXPIRES_IN="${toEnvSafe(result.expiresIn || '')}"`
|
|
195
|
-
];
|
|
196
|
-
fs.writeFileSync(path.resolve(outputPath), `${lines.join('\n')}\n`, 'utf8');
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
module.exports = {
|
|
200
|
-
getLocalOAuthClientPath,
|
|
201
|
-
readStoredOAuthClient,
|
|
202
|
-
writeStoredOAuthClient,
|
|
203
|
-
clearStoredOAuthClient,
|
|
204
|
-
maskSecret,
|
|
205
|
-
generateRandomString,
|
|
206
|
-
base64url,
|
|
207
|
-
generatePKCE,
|
|
208
|
-
decodeJwtPayload,
|
|
209
|
-
isJwtToken,
|
|
210
|
-
isJwtExpired,
|
|
211
|
-
getHeadlessTokenPath,
|
|
212
|
-
getOAuthSessionPath,
|
|
213
|
-
readOAuthSession,
|
|
214
|
-
writeOAuthSession,
|
|
215
|
-
clearOAuthSession,
|
|
216
|
-
readHeadlessToken,
|
|
217
|
-
getAccessTokenExpiryMs,
|
|
218
|
-
hasFreshAccessToken,
|
|
219
|
-
persistAccessToken,
|
|
220
|
-
toEnvSafe,
|
|
221
|
-
writeHeadlessEnvFile
|
|
222
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { readJsonIfExists, writeFile, ensureDir } = require('./fsUtils');
|
|
7
|
+
const { getEnv } = require('./env');
|
|
8
|
+
const { runResolvedCommand } = require('./fsUtils');
|
|
9
|
+
|
|
10
|
+
function getLocalOAuthClientPath() {
|
|
11
|
+
return path.join(os.homedir(), '.empirical', 'oauth-client.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readStoredOAuthClient() {
|
|
15
|
+
const stored = readJsonIfExists(getLocalOAuthClientPath());
|
|
16
|
+
if (!stored || typeof stored !== 'object') return null;
|
|
17
|
+
const clientId = String(stored.clientId || '').trim();
|
|
18
|
+
if (!clientId) return null;
|
|
19
|
+
return {
|
|
20
|
+
clientId,
|
|
21
|
+
clientSecret: String(stored.clientSecret || '')
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function writeStoredOAuthClient(clientId, clientSecret) {
|
|
26
|
+
const payload = {
|
|
27
|
+
clientId: String(clientId || '').trim(),
|
|
28
|
+
clientSecret: String(clientSecret || '').trim(),
|
|
29
|
+
updatedAt: new Date().toISOString()
|
|
30
|
+
};
|
|
31
|
+
writeFile(getLocalOAuthClientPath(), `${JSON.stringify(payload, null, 2)}\n`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function clearStoredOAuthClient() {
|
|
35
|
+
const storePath = getLocalOAuthClientPath();
|
|
36
|
+
if (fs.existsSync(storePath)) {
|
|
37
|
+
fs.unlinkSync(storePath);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function maskSecret(value) {
|
|
42
|
+
const raw = String(value || '');
|
|
43
|
+
if (!raw) return '(not set)';
|
|
44
|
+
if (raw.length <= 8) return '*'.repeat(raw.length);
|
|
45
|
+
return `${raw.slice(0, 4)}...${raw.slice(-4)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function generateRandomString(length = 32) {
|
|
49
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
50
|
+
let result = '';
|
|
51
|
+
for (let i = 0; i < length; i += 1) {
|
|
52
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function base64url(buf) {
|
|
58
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function generatePKCE() {
|
|
62
|
+
const crypto = require('crypto');
|
|
63
|
+
const codeVerifier = generateRandomString(128);
|
|
64
|
+
const codeChallenge = base64url(crypto.createHash('sha256').update(codeVerifier).digest());
|
|
65
|
+
return { codeVerifier, codeChallenge };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function decodeJwtPayload(token) {
|
|
69
|
+
try {
|
|
70
|
+
const parts = String(token).split('.');
|
|
71
|
+
if (parts.length !== 3) return null;
|
|
72
|
+
return JSON.parse(Buffer.from(parts[1], 'base64url').toString());
|
|
73
|
+
} catch (_error) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isJwtToken(token) {
|
|
79
|
+
return typeof token === 'string' && token.split('.').length === 3;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isJwtExpired(token, skewSeconds = 30) {
|
|
83
|
+
const payload = decodeJwtPayload(token);
|
|
84
|
+
if (!payload || typeof payload.exp !== 'number') return true;
|
|
85
|
+
return payload.exp < Math.floor(Date.now() / 1000) + skewSeconds;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getHeadlessTokenPath() {
|
|
89
|
+
return path.resolve(getEnv('EMPIRICAL_TOKEN_OUTPUT', '.empirical/headless-token.json'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getOAuthSessionPath() {
|
|
93
|
+
return path.join(os.homedir(), '.empirical', 'oauth-session.json');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readOAuthSession() {
|
|
97
|
+
const session = readJsonIfExists(getOAuthSessionPath());
|
|
98
|
+
if (!session || typeof session !== 'object') return null;
|
|
99
|
+
return session;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function writeOAuthSession(session) {
|
|
103
|
+
if (!session?.accessToken) return;
|
|
104
|
+
writeFile(getOAuthSessionPath(), `${JSON.stringify(session, null, 2)}\n`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function clearOAuthSession() {
|
|
108
|
+
const sessionPath = getOAuthSessionPath();
|
|
109
|
+
if (fs.existsSync(sessionPath)) fs.unlinkSync(sessionPath);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function readHeadlessToken() {
|
|
113
|
+
const tokenPath = getHeadlessTokenPath();
|
|
114
|
+
if (!fs.existsSync(tokenPath)) return null;
|
|
115
|
+
try {
|
|
116
|
+
const parsed = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
|
|
117
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const normalized = { ...parsed };
|
|
122
|
+
if (!normalized.clientId && normalized.authClientId) {
|
|
123
|
+
normalized.clientId = normalized.authClientId;
|
|
124
|
+
}
|
|
125
|
+
if (!normalized.clientSecret && normalized.authClientSecret) {
|
|
126
|
+
normalized.clientSecret = normalized.authClientSecret;
|
|
127
|
+
}
|
|
128
|
+
if (!normalized.apiKey) {
|
|
129
|
+
normalized.apiKey = normalized.clientSecret || normalized.authClientSecret || null;
|
|
130
|
+
}
|
|
131
|
+
return normalized;
|
|
132
|
+
} catch (_error) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getAccessTokenExpiryMs(token) {
|
|
138
|
+
if (!token || !token.accessToken) return 0;
|
|
139
|
+
const obtainedAtMs = Date.parse(String(token.obtainedAt || ''));
|
|
140
|
+
const expiresInSeconds = Number(token.expiresIn || 0);
|
|
141
|
+
if (!Number.isFinite(obtainedAtMs) || !Number.isFinite(expiresInSeconds) || expiresInSeconds <= 0) {
|
|
142
|
+
return 0;
|
|
143
|
+
}
|
|
144
|
+
return obtainedAtMs + (expiresInSeconds * 1000);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function hasFreshAccessToken(token, skewSeconds = 120) {
|
|
148
|
+
const expiryMs = getAccessTokenExpiryMs(token);
|
|
149
|
+
if (!expiryMs) return false;
|
|
150
|
+
return expiryMs > (Date.now() + (skewSeconds * 1000));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function persistAccessToken(accessToken) {
|
|
154
|
+
if (!accessToken) return;
|
|
155
|
+
process.env.EMPIRICAL_ACCESS_TOKEN = accessToken;
|
|
156
|
+
if (process.platform !== 'win32') {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// `setx` writes permanently into the user's Windows environment, surviving
|
|
160
|
+
// every future shell/process. Only do that for a genuinely permanent
|
|
161
|
+
// credential (a plain apiKey) — a short-lived session JWT written here
|
|
162
|
+
// would silently outlive its own expiry and shadow a valid apiKey in the
|
|
163
|
+
// token file for good, forcing a manual env-var reset roughly every hour.
|
|
164
|
+
if (isJwtToken(accessToken)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const escaped = String(accessToken).replace(/\"/g, '""');
|
|
168
|
+
runResolvedCommand('setx', ['EMPIRICAL_ACCESS_TOKEN', escaped], { stdio: 'ignore' });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function toEnvSafe(value) {
|
|
172
|
+
return String(value ?? '')
|
|
173
|
+
.replace(/\\/g, '\\\\')
|
|
174
|
+
.replace(/\n/g, '\\n')
|
|
175
|
+
.replace(/\r/g, '\\r')
|
|
176
|
+
.replace(/"/g, '\\"');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function writeHeadlessEnvFile(result, outputPath) {
|
|
180
|
+
ensureDir(path.dirname(path.resolve(outputPath)));
|
|
181
|
+
// Prefer the API key as the access token — it never expires and works as a
|
|
182
|
+
// permanent Bearer credential with the MCP endpoint.
|
|
183
|
+
const permanentToken = result.apiKey || result.accessToken;
|
|
184
|
+
const lines = [
|
|
185
|
+
'# Generated by empirical cli',
|
|
186
|
+
`EMPIRICAL_BASE_URL="${toEnvSafe(result.baseUrl)}"`,
|
|
187
|
+
`EMPIRICAL_ACCESS_TOKEN="${toEnvSafe(permanentToken)}"`,
|
|
188
|
+
`EMPIRICAL_API_KEY="${toEnvSafe(result.apiKey || '')}"`,
|
|
189
|
+
`EMPIRICAL_REFRESH_TOKEN="${toEnvSafe(result.refreshToken || '')}"`,
|
|
190
|
+
`EMPIRICAL_CLIENT_ID="${toEnvSafe(result.clientId || '')}"`,
|
|
191
|
+
`EMPIRICAL_CLIENT_SECRET="${toEnvSafe(result.clientSecret || '')}"`,
|
|
192
|
+
`EMPIRICAL_TOKEN_TYPE="${toEnvSafe(result.tokenType || 'Bearer')}"`,
|
|
193
|
+
`EMPIRICAL_TOKEN_OBTAINED_AT="${toEnvSafe(result.obtainedAt || '')}"`,
|
|
194
|
+
`EMPIRICAL_TOKEN_EXPIRES_IN="${toEnvSafe(result.expiresIn || '')}"`
|
|
195
|
+
];
|
|
196
|
+
fs.writeFileSync(path.resolve(outputPath), `${lines.join('\n')}\n`, 'utf8');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
getLocalOAuthClientPath,
|
|
201
|
+
readStoredOAuthClient,
|
|
202
|
+
writeStoredOAuthClient,
|
|
203
|
+
clearStoredOAuthClient,
|
|
204
|
+
maskSecret,
|
|
205
|
+
generateRandomString,
|
|
206
|
+
base64url,
|
|
207
|
+
generatePKCE,
|
|
208
|
+
decodeJwtPayload,
|
|
209
|
+
isJwtToken,
|
|
210
|
+
isJwtExpired,
|
|
211
|
+
getHeadlessTokenPath,
|
|
212
|
+
getOAuthSessionPath,
|
|
213
|
+
readOAuthSession,
|
|
214
|
+
writeOAuthSession,
|
|
215
|
+
clearOAuthSession,
|
|
216
|
+
readHeadlessToken,
|
|
217
|
+
getAccessTokenExpiryMs,
|
|
218
|
+
hasFreshAccessToken,
|
|
219
|
+
persistAccessToken,
|
|
220
|
+
toEnvSafe,
|
|
221
|
+
writeHeadlessEnvFile
|
|
222
|
+
};
|