rush-ai 0.18.0 → 0.19.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 +21 -3
- package/dist/_codex-content-loader-Q7KBWZSB.js +16 -0
- package/dist/_codex-content-loader-Q7KBWZSB.js.map +1 -0
- package/dist/chunk-2AICQRQP.js +447 -0
- package/dist/chunk-2AICQRQP.js.map +1 -0
- package/dist/{chunk-YKZIRW26.js → chunk-E6OSONYW.js} +14 -332
- package/dist/chunk-E6OSONYW.js.map +1 -0
- package/dist/chunk-OIKYNVKO.js +291 -0
- package/dist/chunk-OIKYNVKO.js.map +1 -0
- package/dist/chunk-RLKEUPBP.js +992 -0
- package/dist/chunk-RLKEUPBP.js.map +1 -0
- package/dist/chunk-T5S6NCHZ.js +48 -0
- package/dist/chunk-T5S6NCHZ.js.map +1 -0
- package/dist/chunk-X45FKY3L.js +98 -0
- package/dist/chunk-X45FKY3L.js.map +1 -0
- package/dist/index.js +691 -2010
- package/dist/index.js.map +1 -1
- package/dist/{install-IH2VPYXH.js → install-JQN4BIHX.js} +6 -3
- package/dist/{install-IH2VPYXH.js.map → install-JQN4BIHX.js.map} +1 -1
- package/dist/mcp-UZYID3GG.js +20 -0
- package/dist/mcp-UZYID3GG.js.map +1 -0
- package/dist/mirror-IHLOSPAS.js +17 -0
- package/dist/mirror-IHLOSPAS.js.map +1 -0
- package/dist/{server-Y2CXHDCK.js → server-PGXKRIPY.js} +6 -3
- package/dist/{server-Y2CXHDCK.js.map → server-PGXKRIPY.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-YKZIRW26.js.map +0 -1
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/util/config.ts
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import Conf from "conf";
|
|
7
|
+
var AUTH_CONFIG_DIR = resolve(homedir(), ".rush");
|
|
8
|
+
var DEFAULT_API = "https://rush.zhenguanyu.com";
|
|
9
|
+
var AUTH_DEFAULTS = {
|
|
10
|
+
token: null,
|
|
11
|
+
expiresAt: null,
|
|
12
|
+
refreshToken: null,
|
|
13
|
+
method: null,
|
|
14
|
+
tokenId: null,
|
|
15
|
+
sourceUrl: null
|
|
16
|
+
};
|
|
17
|
+
var GLOBAL_DEFAULTS = {
|
|
18
|
+
currentTeam: null,
|
|
19
|
+
api: DEFAULT_API,
|
|
20
|
+
collectMetrics: true
|
|
21
|
+
};
|
|
22
|
+
function isEmptyAuthEntry(entry) {
|
|
23
|
+
if (!entry || typeof entry !== "object") return false;
|
|
24
|
+
const auth = entry;
|
|
25
|
+
return !auth.token && auth.expiresAt == null && !auth.refreshToken && auth.method == null && !auth.tokenId && !auth.sourceUrl;
|
|
26
|
+
}
|
|
27
|
+
var globalStore = new Conf({
|
|
28
|
+
projectName: "rush-ai",
|
|
29
|
+
cwd: AUTH_CONFIG_DIR,
|
|
30
|
+
configName: "config",
|
|
31
|
+
defaults: {}
|
|
32
|
+
});
|
|
33
|
+
var authStore = new Conf({
|
|
34
|
+
projectName: "rush-ai",
|
|
35
|
+
cwd: AUTH_CONFIG_DIR,
|
|
36
|
+
configName: "auth",
|
|
37
|
+
defaults: {}
|
|
38
|
+
});
|
|
39
|
+
function migrateGlobalStoreIfNeeded() {
|
|
40
|
+
const version = globalStore.get("_version");
|
|
41
|
+
if (version === 2) return;
|
|
42
|
+
const hasOldFormat = globalStore.has("api") && !globalStore.has("profiles");
|
|
43
|
+
const isEmpty = globalStore.size === 0;
|
|
44
|
+
if (hasOldFormat) {
|
|
45
|
+
const oldConfig = {
|
|
46
|
+
currentTeam: globalStore.get("currentTeam") ?? null,
|
|
47
|
+
api: globalStore.get("api") ?? DEFAULT_API,
|
|
48
|
+
collectMetrics: globalStore.get("collectMetrics") ?? true
|
|
49
|
+
};
|
|
50
|
+
globalStore.clear();
|
|
51
|
+
globalStore.set("_version", 2);
|
|
52
|
+
globalStore.set("activeProfile", "default");
|
|
53
|
+
globalStore.set("profiles", { default: oldConfig });
|
|
54
|
+
} else if (isEmpty || !globalStore.has("profiles")) {
|
|
55
|
+
globalStore.set("_version", 2);
|
|
56
|
+
if (!globalStore.has("activeProfile")) {
|
|
57
|
+
globalStore.set("activeProfile", "default");
|
|
58
|
+
}
|
|
59
|
+
if (!globalStore.has("profiles")) {
|
|
60
|
+
globalStore.set("profiles", { default: { ...GLOBAL_DEFAULTS } });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function migrateAuthStoreIfNeeded() {
|
|
65
|
+
const version = authStore.get("_version");
|
|
66
|
+
if (version === 3) {
|
|
67
|
+
pruneEmptyAuthEntries();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const hasOldFormat = authStore.has("token") || authStore.has("method");
|
|
71
|
+
const isEmpty = authStore.size === 0;
|
|
72
|
+
if (hasOldFormat) {
|
|
73
|
+
const oldAuth = {
|
|
74
|
+
token: authStore.get("token") ?? null,
|
|
75
|
+
expiresAt: authStore.get("expiresAt") ?? null,
|
|
76
|
+
refreshToken: authStore.get("refreshToken") ?? null,
|
|
77
|
+
method: authStore.get("method") ?? null,
|
|
78
|
+
tokenId: authStore.get("tokenId") ?? null,
|
|
79
|
+
sourceUrl: null
|
|
80
|
+
};
|
|
81
|
+
authStore.clear();
|
|
82
|
+
authStore.set("_version", 3);
|
|
83
|
+
if (!isEmptyAuthEntry(oldAuth)) {
|
|
84
|
+
authStore.set("default", oldAuth);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (isEmpty) {
|
|
89
|
+
authStore.set("_version", 3);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (version === 2) {
|
|
93
|
+
for (const key of Object.keys(authStore.store)) {
|
|
94
|
+
if (key === "_version") continue;
|
|
95
|
+
const entry = authStore.get(key);
|
|
96
|
+
if (!entry || typeof entry !== "object") continue;
|
|
97
|
+
const asAuth = entry;
|
|
98
|
+
if (!("sourceUrl" in asAuth)) {
|
|
99
|
+
authStore.set(key, { ...asAuth, sourceUrl: null });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
authStore.set("_version", 3);
|
|
103
|
+
pruneEmptyAuthEntries();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function pruneEmptyAuthEntries() {
|
|
107
|
+
for (const key of Object.keys(authStore.store)) {
|
|
108
|
+
if (key === "_version") continue;
|
|
109
|
+
if (isEmptyAuthEntry(authStore.get(key))) {
|
|
110
|
+
authStore.delete(key);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
migrateGlobalStoreIfNeeded();
|
|
115
|
+
migrateAuthStoreIfNeeded();
|
|
116
|
+
function getActiveProfile() {
|
|
117
|
+
return process.env.RUSH_PROFILE ?? globalStore.get("activeProfile") ?? "default";
|
|
118
|
+
}
|
|
119
|
+
function setActiveProfile(name) {
|
|
120
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
121
|
+
if (!profiles[name]) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Profile '${name}' does not exist. Available: ${Object.keys(profiles).join(", ")}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
globalStore.set("activeProfile", name);
|
|
127
|
+
}
|
|
128
|
+
function listProfiles() {
|
|
129
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
130
|
+
return Object.keys(profiles);
|
|
131
|
+
}
|
|
132
|
+
function createProfile(name, config) {
|
|
133
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
134
|
+
if (profiles[name]) {
|
|
135
|
+
throw new Error(`Profile '${name}' already exists.`);
|
|
136
|
+
}
|
|
137
|
+
const defaultConfig = profiles.default ?? GLOBAL_DEFAULTS;
|
|
138
|
+
profiles[name] = { ...defaultConfig, ...config };
|
|
139
|
+
globalStore.set("profiles", profiles);
|
|
140
|
+
}
|
|
141
|
+
function deleteProfile(name) {
|
|
142
|
+
if (name === "default") {
|
|
143
|
+
throw new Error("Cannot delete the 'default' profile.");
|
|
144
|
+
}
|
|
145
|
+
const active = getActiveProfile();
|
|
146
|
+
if (name === active) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Cannot delete the active profile '${name}'. Switch to another profile first.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
152
|
+
if (!profiles[name]) {
|
|
153
|
+
throw new Error(`Profile '${name}' does not exist.`);
|
|
154
|
+
}
|
|
155
|
+
delete profiles[name];
|
|
156
|
+
globalStore.set("profiles", profiles);
|
|
157
|
+
authStore.delete(name);
|
|
158
|
+
}
|
|
159
|
+
function getAuthConfig() {
|
|
160
|
+
const profile = getActiveProfile();
|
|
161
|
+
const profileAuth = authStore.get(profile);
|
|
162
|
+
return {
|
|
163
|
+
token: profileAuth?.token ?? null,
|
|
164
|
+
expiresAt: profileAuth?.expiresAt ?? null,
|
|
165
|
+
refreshToken: profileAuth?.refreshToken ?? null,
|
|
166
|
+
method: profileAuth?.method ?? null,
|
|
167
|
+
tokenId: profileAuth?.tokenId ?? null,
|
|
168
|
+
sourceUrl: profileAuth?.sourceUrl ?? null
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function setAuthConfig(config) {
|
|
172
|
+
const profile = getActiveProfile();
|
|
173
|
+
const current = authStore.get(profile) ?? {
|
|
174
|
+
...AUTH_DEFAULTS
|
|
175
|
+
};
|
|
176
|
+
authStore.set(profile, { ...current, ...config });
|
|
177
|
+
}
|
|
178
|
+
function clearAuthConfig() {
|
|
179
|
+
const profile = getActiveProfile();
|
|
180
|
+
authStore.delete(profile);
|
|
181
|
+
}
|
|
182
|
+
function getGlobalConfig() {
|
|
183
|
+
const profile = getActiveProfile();
|
|
184
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
185
|
+
const profileConfig = profiles[profile] ?? GLOBAL_DEFAULTS;
|
|
186
|
+
return {
|
|
187
|
+
currentTeam: profileConfig.currentTeam ?? null,
|
|
188
|
+
api: profileConfig.api ?? DEFAULT_API,
|
|
189
|
+
collectMetrics: profileConfig.collectMetrics ?? true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function setGlobalConfig(config) {
|
|
193
|
+
const profile = getActiveProfile();
|
|
194
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
195
|
+
const current = profiles[profile] ?? { ...GLOBAL_DEFAULTS };
|
|
196
|
+
profiles[profile] = { ...current, ...config };
|
|
197
|
+
globalStore.set("profiles", profiles);
|
|
198
|
+
}
|
|
199
|
+
function getConfigDir() {
|
|
200
|
+
return AUTH_CONFIG_DIR;
|
|
201
|
+
}
|
|
202
|
+
function isLoggedIn() {
|
|
203
|
+
const auth = getAuthConfig();
|
|
204
|
+
if (!auth.token) return false;
|
|
205
|
+
if (auth.expiresAt && Date.now() > auth.expiresAt) {
|
|
206
|
+
return auth.method === "cas" && Boolean(auth.refreshToken);
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
function normalizeApiUrl(url) {
|
|
211
|
+
return url.replace(/\/+$/, "").toLowerCase();
|
|
212
|
+
}
|
|
213
|
+
function checkAuthSourceMismatch(overrideCurrentUrl) {
|
|
214
|
+
const auth = getAuthConfig();
|
|
215
|
+
if (!auth.token || !auth.sourceUrl) return null;
|
|
216
|
+
const current = overrideCurrentUrl ?? getGlobalConfig().api;
|
|
217
|
+
if (normalizeApiUrl(auth.sourceUrl) === normalizeApiUrl(current)) return null;
|
|
218
|
+
return { stored: auth.sourceUrl, current };
|
|
219
|
+
}
|
|
220
|
+
function getProfileConfig(name) {
|
|
221
|
+
const profiles = globalStore.get("profiles") ?? {};
|
|
222
|
+
return profiles[name] ?? null;
|
|
223
|
+
}
|
|
224
|
+
function getProfileAuth(name) {
|
|
225
|
+
const profileAuth = authStore.get(name);
|
|
226
|
+
return {
|
|
227
|
+
token: profileAuth?.token ?? null,
|
|
228
|
+
expiresAt: profileAuth?.expiresAt ?? null,
|
|
229
|
+
refreshToken: profileAuth?.refreshToken ?? null,
|
|
230
|
+
method: profileAuth?.method ?? null,
|
|
231
|
+
tokenId: profileAuth?.tokenId ?? null,
|
|
232
|
+
sourceUrl: profileAuth?.sourceUrl ?? null
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/util/auth.ts
|
|
237
|
+
function getAuthMethod() {
|
|
238
|
+
if (process.env.RUSH_API_KEY) {
|
|
239
|
+
return "api_key";
|
|
240
|
+
}
|
|
241
|
+
const auth = getAuthConfig();
|
|
242
|
+
if (auth.token) {
|
|
243
|
+
if (auth.method === "api_key" || auth.token.startsWith("rush_sk_")) {
|
|
244
|
+
return "api_key";
|
|
245
|
+
}
|
|
246
|
+
if (auth.method === "platform_token") {
|
|
247
|
+
return "platform_token";
|
|
248
|
+
}
|
|
249
|
+
return auth.method ?? "cas";
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function getAuthToken() {
|
|
254
|
+
if (process.env.RUSH_API_KEY) {
|
|
255
|
+
return process.env.RUSH_API_KEY;
|
|
256
|
+
}
|
|
257
|
+
const auth = getAuthConfig();
|
|
258
|
+
return auth.token;
|
|
259
|
+
}
|
|
260
|
+
function getCasConfig() {
|
|
261
|
+
const config = getGlobalConfig();
|
|
262
|
+
const baseUrl = process.env.RUSH_API_URL ?? config.api;
|
|
263
|
+
return {
|
|
264
|
+
authorizeEndpoint: process.env.RUSH_CAS_AUTHORIZE_ENDPOINT ?? `${baseUrl}/cas/oauth2/authorize`,
|
|
265
|
+
tokenEndpoint: process.env.RUSH_CAS_TOKEN_ENDPOINT ?? `${baseUrl}/cas/oauth2/token`,
|
|
266
|
+
revokeEndpoint: process.env.RUSH_CAS_REVOKE_ENDPOINT ?? `${baseUrl}/cas/oauth2/revoke`,
|
|
267
|
+
clientId: process.env.RUSH_CAS_CLIENT_ID ?? "rush-ai"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export {
|
|
272
|
+
getActiveProfile,
|
|
273
|
+
setActiveProfile,
|
|
274
|
+
listProfiles,
|
|
275
|
+
createProfile,
|
|
276
|
+
deleteProfile,
|
|
277
|
+
getAuthConfig,
|
|
278
|
+
setAuthConfig,
|
|
279
|
+
clearAuthConfig,
|
|
280
|
+
getGlobalConfig,
|
|
281
|
+
setGlobalConfig,
|
|
282
|
+
getConfigDir,
|
|
283
|
+
isLoggedIn,
|
|
284
|
+
checkAuthSourceMismatch,
|
|
285
|
+
getProfileConfig,
|
|
286
|
+
getProfileAuth,
|
|
287
|
+
getAuthMethod,
|
|
288
|
+
getAuthToken,
|
|
289
|
+
getCasConfig
|
|
290
|
+
};
|
|
291
|
+
//# sourceMappingURL=chunk-OIKYNVKO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/config.ts","../src/util/auth.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { resolve } from 'node:path';\nimport Conf from 'conf';\n\nexport interface AuthConfig {\n token: string | null;\n expiresAt: number | null;\n refreshToken: string | null;\n /** How the token was obtained */\n method: 'cas' | 'api_key' | 'platform_token' | null;\n /** Platform Token jti (for remote revocation via DELETE /api/platform-tokens?id={tokenId}) */\n tokenId: string | null;\n /**\n * API base URL the token was issued against. Stored at login so we can\n * detect when the current profile's API URL drifts from where the token\n * came from (e.g. `config set api` pointed at a different environment).\n * `null` for tokens migrated from the v2 schema — treated as \"unknown\n * origin\", so we don't flag false positives on upgrade.\n */\n sourceUrl: string | null;\n}\n\nexport interface GlobalConfig {\n currentTeam: string | null;\n api: string;\n collectMetrics: boolean;\n}\n\nexport interface ProjectConfig {\n projectId: string | null;\n orgId: string | null;\n}\n\n// --- Profile store types ---\n\ninterface ProfiledGlobalStore {\n _version: number;\n activeProfile: string;\n profiles: Record<string, GlobalConfig>;\n}\n\ninterface ProfiledAuthStore {\n _version: number;\n [profileName: string]: AuthConfig | number; // profile entries + _version\n}\n\nconst AUTH_CONFIG_DIR = resolve(homedir(), '.rush');\nconst DEFAULT_API = 'https://rush.zhenguanyu.com';\n\nconst AUTH_DEFAULTS: AuthConfig = {\n token: null,\n expiresAt: null,\n refreshToken: null,\n method: null,\n tokenId: null,\n sourceUrl: null,\n};\n\nconst GLOBAL_DEFAULTS: GlobalConfig = {\n currentTeam: null,\n api: DEFAULT_API,\n collectMetrics: true,\n};\n\nfunction isEmptyAuthEntry(entry: unknown): boolean {\n if (!entry || typeof entry !== 'object') return false;\n const auth = entry as Partial<AuthConfig>;\n return (\n !auth.token &&\n auth.expiresAt == null &&\n !auth.refreshToken &&\n auth.method == null &&\n !auth.tokenId &&\n !auth.sourceUrl\n );\n}\n\n// --- Raw stores (untyped to support migration) ---\n\nconst globalStore = new Conf<Record<string, unknown>>({\n projectName: 'rush-ai',\n cwd: AUTH_CONFIG_DIR,\n configName: 'config',\n defaults: {},\n});\n\nconst authStore = new Conf<Record<string, unknown>>({\n projectName: 'rush-ai',\n cwd: AUTH_CONFIG_DIR,\n configName: 'auth',\n defaults: {},\n});\n\n// --- Migration ---\n\nfunction migrateGlobalStoreIfNeeded(): void {\n const version = globalStore.get('_version');\n if (version === 2) return; // Already migrated\n\n // Check if this is old format (has 'api' at top level but no 'profiles')\n const hasOldFormat = globalStore.has('api') && !globalStore.has('profiles');\n const isEmpty = globalStore.size === 0;\n\n if (hasOldFormat) {\n // Migrate old flat format → profile format\n const oldConfig: GlobalConfig = {\n currentTeam: (globalStore.get('currentTeam') as string | null) ?? null,\n api: (globalStore.get('api') as string) ?? DEFAULT_API,\n collectMetrics: (globalStore.get('collectMetrics') as boolean) ?? true,\n };\n\n globalStore.clear();\n globalStore.set('_version', 2);\n globalStore.set('activeProfile', 'default');\n globalStore.set('profiles', { default: oldConfig });\n } else if (isEmpty || !globalStore.has('profiles')) {\n // Fresh install or incomplete state — initialize\n globalStore.set('_version', 2);\n if (!globalStore.has('activeProfile')) {\n globalStore.set('activeProfile', 'default');\n }\n if (!globalStore.has('profiles')) {\n globalStore.set('profiles', { default: { ...GLOBAL_DEFAULTS } });\n }\n }\n}\n\nfunction migrateAuthStoreIfNeeded(): void {\n const version = authStore.get('_version');\n if (version === 3) {\n pruneEmptyAuthEntries();\n return;\n }\n\n // v1 → v3: legacy flat format (`token` at top level).\n const hasOldFormat = authStore.has('token') || authStore.has('method');\n const isEmpty = authStore.size === 0;\n\n if (hasOldFormat) {\n const oldAuth: AuthConfig = {\n token: (authStore.get('token') as string | null) ?? null,\n expiresAt: (authStore.get('expiresAt') as number | null) ?? null,\n refreshToken: (authStore.get('refreshToken') as string | null) ?? null,\n method: (authStore.get('method') as AuthConfig['method']) ?? null,\n tokenId: (authStore.get('tokenId') as string | null) ?? null,\n sourceUrl: null,\n };\n\n authStore.clear();\n authStore.set('_version', 3);\n if (!isEmptyAuthEntry(oldAuth)) {\n authStore.set('default', oldAuth);\n }\n return;\n }\n\n if (isEmpty) {\n authStore.set('_version', 3);\n return;\n }\n\n // v2 → v3: profile-shaped but missing `sourceUrl`. Backfill null.\n if (version === 2) {\n for (const key of Object.keys(authStore.store)) {\n if (key === '_version') continue;\n const entry = authStore.get(key);\n if (!entry || typeof entry !== 'object') continue;\n const asAuth = entry as Record<string, unknown>;\n if (!('sourceUrl' in asAuth)) {\n authStore.set(key, { ...asAuth, sourceUrl: null });\n }\n }\n authStore.set('_version', 3);\n pruneEmptyAuthEntries();\n }\n}\n\nfunction pruneEmptyAuthEntries(): void {\n for (const key of Object.keys(authStore.store)) {\n if (key === '_version') continue;\n if (isEmptyAuthEntry(authStore.get(key))) {\n authStore.delete(key);\n }\n }\n}\n\n// Run migrations on module load\nmigrateGlobalStoreIfNeeded();\nmigrateAuthStoreIfNeeded();\n\n// --- Profile helpers ---\n\n/**\n * Get the active profile name.\n * RUSH_PROFILE env var takes precedence over stored activeProfile.\n */\nexport function getActiveProfile(): string {\n return (\n process.env.RUSH_PROFILE ??\n (globalStore.get('activeProfile') as string) ??\n 'default'\n );\n}\n\nexport function setActiveProfile(name: string): void {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (!profiles[name]) {\n throw new Error(\n `Profile '${name}' does not exist. Available: ${Object.keys(profiles).join(', ')}`\n );\n }\n globalStore.set('activeProfile', name);\n}\n\nexport function listProfiles(): string[] {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n return Object.keys(profiles);\n}\n\nexport function createProfile(\n name: string,\n config?: Partial<GlobalConfig>\n): void {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (profiles[name]) {\n throw new Error(`Profile '${name}' already exists.`);\n }\n // Inherit from default profile\n const defaultConfig = profiles.default ?? GLOBAL_DEFAULTS;\n profiles[name] = { ...defaultConfig, ...config };\n globalStore.set('profiles', profiles);\n}\n\nexport function deleteProfile(name: string): void {\n if (name === 'default') {\n throw new Error(\"Cannot delete the 'default' profile.\");\n }\n const active = getActiveProfile();\n if (name === active) {\n throw new Error(\n `Cannot delete the active profile '${name}'. Switch to another profile first.`\n );\n }\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n if (!profiles[name]) {\n throw new Error(`Profile '${name}' does not exist.`);\n }\n delete profiles[name];\n globalStore.set('profiles', profiles);\n\n // Also clear auth for this profile\n authStore.delete(name);\n}\n\n// --- Auth config (profile-aware) ---\n\nexport function getAuthConfig(): AuthConfig {\n const profile = getActiveProfile();\n const profileAuth = authStore.get(profile) as AuthConfig | undefined;\n return {\n token: profileAuth?.token ?? null,\n expiresAt: profileAuth?.expiresAt ?? null,\n refreshToken: profileAuth?.refreshToken ?? null,\n method: profileAuth?.method ?? null,\n tokenId: profileAuth?.tokenId ?? null,\n sourceUrl: profileAuth?.sourceUrl ?? null,\n };\n}\n\nexport function setAuthConfig(config: Partial<AuthConfig>): void {\n const profile = getActiveProfile();\n const current = (authStore.get(profile) as AuthConfig) ?? {\n ...AUTH_DEFAULTS,\n };\n authStore.set(profile, { ...current, ...config });\n}\n\n/** Clear auth for the current profile only */\nexport function clearAuthConfig(): void {\n const profile = getActiveProfile();\n authStore.delete(profile);\n}\n\n/** Clear auth for ALL profiles */\nexport function clearAllAuthConfig(): void {\n for (const key of Object.keys(authStore.store)) {\n if (key !== '_version') {\n authStore.delete(key);\n }\n }\n}\n\n// --- Global config (profile-aware) ---\n\nexport function getGlobalConfig(): GlobalConfig {\n const profile = getActiveProfile();\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n const profileConfig = profiles[profile] ?? GLOBAL_DEFAULTS;\n return {\n currentTeam: profileConfig.currentTeam ?? null,\n api: profileConfig.api ?? DEFAULT_API,\n collectMetrics: profileConfig.collectMetrics ?? true,\n };\n}\n\nexport function setGlobalConfig(config: Partial<GlobalConfig>): void {\n const profile = getActiveProfile();\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n const current = profiles[profile] ?? { ...GLOBAL_DEFAULTS };\n profiles[profile] = { ...current, ...config };\n globalStore.set('profiles', profiles);\n}\n\nexport function getConfigDir(): string {\n return AUTH_CONFIG_DIR;\n}\n\nexport function isLoggedIn(): boolean {\n const auth = getAuthConfig();\n if (!auth.token) return false;\n if (auth.expiresAt && Date.now() > auth.expiresAt) {\n // CAS tokens with refresh_token can still be renewed lazily by the client.\n return auth.method === 'cas' && Boolean(auth.refreshToken);\n }\n return true;\n}\n\n/** Normalize an API URL for comparison — strips trailing slashes + lowercases. */\nexport function normalizeApiUrl(url: string): string {\n return url.replace(/\\/+$/, '').toLowerCase();\n}\n\n/**\n * If the current profile's token was issued against a different API URL than\n * the profile currently points to, return both sides so the caller can prompt\n * the user to re-authenticate. Returns null when no mismatch (or when the\n * stored `sourceUrl` is null — migrated tokens with unknown origin). The\n * caller decides what to do; this function does NOT mutate or clear anything.\n *\n * @param overrideCurrentUrl use this URL instead of reading the current\n * global config. Useful when evaluating mismatch BEFORE writing a new API\n * URL in `config set`.\n */\nexport function checkAuthSourceMismatch(\n overrideCurrentUrl?: string\n): { stored: string; current: string } | null {\n const auth = getAuthConfig();\n if (!auth.token || !auth.sourceUrl) return null;\n const current = overrideCurrentUrl ?? getGlobalConfig().api;\n if (normalizeApiUrl(auth.sourceUrl) === normalizeApiUrl(current)) return null;\n return { stored: auth.sourceUrl, current };\n}\n\n/**\n * Get the config for a specific profile (for display purposes).\n */\nexport function getProfileConfig(name: string): GlobalConfig | null {\n const profiles =\n (globalStore.get('profiles') as Record<string, GlobalConfig>) ?? {};\n return profiles[name] ?? null;\n}\n\n/**\n * Get auth config for a specific profile (for display purposes).\n */\nexport function getProfileAuth(name: string): AuthConfig {\n const profileAuth = authStore.get(name) as AuthConfig | undefined;\n return {\n token: profileAuth?.token ?? null,\n expiresAt: profileAuth?.expiresAt ?? null,\n refreshToken: profileAuth?.refreshToken ?? null,\n method: profileAuth?.method ?? null,\n tokenId: profileAuth?.tokenId ?? null,\n sourceUrl: profileAuth?.sourceUrl ?? null,\n };\n}\n","import { getAuthConfig, getGlobalConfig } from './config.js';\n\nexport type AuthMethod = 'api_key' | 'cas' | 'platform_token' | null;\n\n/**\n * Determine the current authentication method.\n * Priority: RUSH_API_KEY env > stored CAS token > null\n */\nexport function getAuthMethod(): AuthMethod {\n if (process.env.RUSH_API_KEY) {\n return 'api_key';\n }\n const auth = getAuthConfig();\n if (auth.token) {\n // Use stored method if available; detect rush_sk_ prefix as fallback\n if (auth.method === 'api_key' || auth.token.startsWith('rush_sk_')) {\n return 'api_key';\n }\n if (auth.method === 'platform_token') {\n return 'platform_token';\n }\n return auth.method ?? 'cas';\n }\n return null;\n}\n\n/**\n * Get the active auth token.\n * Priority: RUSH_API_KEY env > stored token\n */\nexport function getAuthToken(): string | null {\n if (process.env.RUSH_API_KEY) {\n return process.env.RUSH_API_KEY;\n }\n const auth = getAuthConfig();\n return auth.token;\n}\n\nexport interface CasConfig {\n authorizeEndpoint: string;\n tokenEndpoint: string;\n revokeEndpoint: string;\n clientId: string;\n}\n\n/**\n * Get CAS PKCE configuration.\n * Supports env overrides for private deployments.\n */\nexport function getCasConfig(): CasConfig {\n const config = getGlobalConfig();\n const baseUrl = process.env.RUSH_API_URL ?? config.api;\n\n return {\n authorizeEndpoint:\n process.env.RUSH_CAS_AUTHORIZE_ENDPOINT ??\n `${baseUrl}/cas/oauth2/authorize`,\n tokenEndpoint:\n process.env.RUSH_CAS_TOKEN_ENDPOINT ?? `${baseUrl}/cas/oauth2/token`,\n revokeEndpoint:\n process.env.RUSH_CAS_REVOKE_ENDPOINT ?? `${baseUrl}/cas/oauth2/revoke`,\n clientId: process.env.RUSH_CAS_CLIENT_ID ?? 'rush-ai',\n };\n}\n\nexport interface Principal {\n provider: 'ldap' | 'service';\n subject: string;\n}\n\n/**\n * Build a Principal object from the current auth context.\n * M1: only ldap is supported.\n */\nexport function getPrincipal(subject: string): Principal {\n const method = getAuthMethod();\n return {\n provider: method === 'api_key' ? 'service' : 'ldap',\n subject,\n };\n}\n"],"mappings":"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","names":[]}
|