@anytio/pspm 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -1
- package/CLI_GUIDE.md +7 -2
- package/README.md +3 -1
- package/dist/add-CcgUlOLa.js +753 -0
- package/dist/add-Cnn-OR9g.js +2 -0
- package/dist/api-client-CBTk37gh.js +2 -0
- package/dist/api-client-DBXUpGoX.js +450 -0
- package/dist/config-BQy_Rjip.js +468 -0
- package/dist/config-BZJ6_GsC.js +2 -0
- package/dist/index.js +2781 -7019
- package/dist/install-gcvbBeWi.js +2 -0
- package/dist/install-lNvqIk5c.js +477 -0
- package/dist/symlinks-BTw8X0GG.js +1832 -0
- package/package.json +14 -12
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import * as ini from "ini";
|
|
5
|
+
//#region ../../packages/shared/errors/src/extract.ts
|
|
6
|
+
const DEFAULT_STATUS_DESCRIPTIONS = {
|
|
7
|
+
400: "Bad Request",
|
|
8
|
+
401: "Unauthorized",
|
|
9
|
+
403: "Forbidden",
|
|
10
|
+
404: "Not Found",
|
|
11
|
+
409: "Conflict",
|
|
12
|
+
422: "Validation Error",
|
|
13
|
+
429: "Too Many Requests",
|
|
14
|
+
500: "Internal Server Error",
|
|
15
|
+
502: "Bad Gateway",
|
|
16
|
+
503: "Service Unavailable"
|
|
17
|
+
};
|
|
18
|
+
function getStatusDescription(status, overrides) {
|
|
19
|
+
return overrides?.[status] ?? DEFAULT_STATUS_DESCRIPTIONS[status] ?? `HTTP ${status}`;
|
|
20
|
+
}
|
|
21
|
+
function extractApiErrorMessage$1(response, fallbackMessage, options = {}) {
|
|
22
|
+
const errorData = response.data;
|
|
23
|
+
const overrides = options.statusDescriptions;
|
|
24
|
+
if (options.debug) {
|
|
25
|
+
const log = options.debugLogger ?? console.log;
|
|
26
|
+
log(`[debug] API response status: ${response.status}`);
|
|
27
|
+
log("[debug] API response data:", errorData);
|
|
28
|
+
}
|
|
29
|
+
if (typeof errorData === "string") {
|
|
30
|
+
if (response.status === 404) return `${fallbackMessage}: ${getStatusDescription(404, overrides)}`;
|
|
31
|
+
return `${fallbackMessage}: ${errorData} (HTTP ${response.status})`;
|
|
32
|
+
}
|
|
33
|
+
if (!errorData || typeof errorData !== "object") return `${fallbackMessage}: ${getStatusDescription(response.status, overrides)}`;
|
|
34
|
+
let errorMessage = errorData.message || fallbackMessage;
|
|
35
|
+
if (errorData.code === "VALIDATION_ERROR" && errorData.details) {
|
|
36
|
+
const issues = errorData.details.issues;
|
|
37
|
+
if (issues && Array.isArray(issues)) errorMessage = `Validation failed:\n${issues.map((issue) => {
|
|
38
|
+
return ` - ${issue.path?.join(".") || "input"}: ${issue.message || "invalid value"}`;
|
|
39
|
+
}).join("\n")}`;
|
|
40
|
+
}
|
|
41
|
+
if (errorData.code && !errorMessage.includes(errorData.code)) errorMessage = `[${errorData.code}] ${errorMessage}`;
|
|
42
|
+
if (response.status >= 400) errorMessage += ` (HTTP ${response.status})`;
|
|
43
|
+
if (errorData.requestId) errorMessage += `\n(Request ID: ${errorData.requestId})`;
|
|
44
|
+
return errorMessage;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/errors.ts
|
|
48
|
+
/**
|
|
49
|
+
* Base error class for PSPM configuration errors
|
|
50
|
+
*/
|
|
51
|
+
var ConfigError = class extends Error {
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = "ConfigError";
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Error thrown when the user is not logged in
|
|
59
|
+
*/
|
|
60
|
+
var NotLoggedInError = class extends ConfigError {
|
|
61
|
+
constructor() {
|
|
62
|
+
super("Not logged in. Run 'pspm login --api-key <key>' first, or set PSPM_API_KEY env var.");
|
|
63
|
+
this.name = "NotLoggedInError";
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const CLI_STATUS_DESCRIPTIONS = {
|
|
67
|
+
400: "Bad Request - The request was malformed",
|
|
68
|
+
401: "Unauthorized - Please run 'pspm login' first",
|
|
69
|
+
403: "Forbidden - You don't have permission for this action",
|
|
70
|
+
404: "Not Found - The endpoint or resource doesn't exist",
|
|
71
|
+
409: "Conflict - The resource already exists or there's a version conflict",
|
|
72
|
+
422: "Validation Error - The request data is invalid",
|
|
73
|
+
429: "Too Many Requests - Please slow down and try again",
|
|
74
|
+
500: "Internal Server Error - Something went wrong on the server",
|
|
75
|
+
502: "Bad Gateway - The server is temporarily unavailable",
|
|
76
|
+
503: "Service Unavailable - The server is temporarily unavailable"
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Extract a human-readable error message from an API response.
|
|
80
|
+
* Thin wrapper over `@anytio/errors/extract` with CLI-tuned status hints.
|
|
81
|
+
*/
|
|
82
|
+
function extractApiErrorMessage(response, fallbackMessage) {
|
|
83
|
+
return extractApiErrorMessage$1(response, fallbackMessage, {
|
|
84
|
+
statusDescriptions: CLI_STATUS_DESCRIPTIONS,
|
|
85
|
+
debug: Boolean(process.env.PSPM_DEBUG),
|
|
86
|
+
debugLogger: (msg, data) => data === void 0 ? console.log(msg) : console.log(msg, JSON.stringify(data, null, 2))
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/config-auth.ts
|
|
91
|
+
/**
|
|
92
|
+
* Get the auth token for a given registry URL.
|
|
93
|
+
* Falls back to the default API key if no registry-specific token is configured.
|
|
94
|
+
*
|
|
95
|
+
* @param config - The resolved configuration
|
|
96
|
+
* @param registryUrl - The registry URL
|
|
97
|
+
* @returns The auth token to use, or undefined if none available
|
|
98
|
+
*/
|
|
99
|
+
function getTokenForRegistry(config, registryUrl) {
|
|
100
|
+
try {
|
|
101
|
+
const host = new URL(registryUrl).host;
|
|
102
|
+
if (config.registryTokens[host]) return config.registryTokens[host];
|
|
103
|
+
return config.apiKey;
|
|
104
|
+
} catch {
|
|
105
|
+
return config.apiKey;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Set credentials (authToken and optionally username/registry)
|
|
110
|
+
*/
|
|
111
|
+
async function setCredentials(authToken, username, registry) {
|
|
112
|
+
const config = await readUserConfig();
|
|
113
|
+
config.authToken = authToken;
|
|
114
|
+
if (username) config.username = username;
|
|
115
|
+
if (registry && registry !== "https://registry.pspm.dev") config.registry = registry;
|
|
116
|
+
await writeUserConfig(config);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Clear credentials (authToken and username)
|
|
120
|
+
*/
|
|
121
|
+
async function clearCredentials() {
|
|
122
|
+
const config = await readUserConfig();
|
|
123
|
+
config.authToken = void 0;
|
|
124
|
+
config.username = void 0;
|
|
125
|
+
await writeUserConfig(config);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if user is logged in
|
|
129
|
+
*/
|
|
130
|
+
async function isLoggedIn() {
|
|
131
|
+
try {
|
|
132
|
+
return !!(await resolveConfig()).apiKey;
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get the API key (throws if not logged in)
|
|
139
|
+
*/
|
|
140
|
+
async function requireApiKey() {
|
|
141
|
+
const resolved = await resolveConfig();
|
|
142
|
+
if (!resolved.apiKey) {
|
|
143
|
+
if (process.env.PSPM_DEBUG) console.log("[config] requireApiKey: No API key found");
|
|
144
|
+
throw new NotLoggedInError();
|
|
145
|
+
}
|
|
146
|
+
if (process.env.PSPM_DEBUG) console.log(`[config] requireApiKey: Got API key (${resolved.apiKey.substring(0, 10)}...)`);
|
|
147
|
+
return resolved.apiKey;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the registry URL
|
|
151
|
+
*/
|
|
152
|
+
async function getRegistryUrl() {
|
|
153
|
+
return (await resolveConfig()).registryUrl;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get the encryption key for a given scope.
|
|
157
|
+
*
|
|
158
|
+
* Checks environment variable first (PSPM_ENCRYPTION_KEY_{SCOPE}),
|
|
159
|
+
* then falls back to ~/.pspmrc encryption-key:{scope}.
|
|
160
|
+
*
|
|
161
|
+
* @param scope - The scope (e.g., "@user/alice" or "@org/acme")
|
|
162
|
+
* @returns The encryption key, or undefined if not configured
|
|
163
|
+
*/
|
|
164
|
+
async function getEncryptionKey(scope) {
|
|
165
|
+
const envSuffix = scope.replace(/^@/, "").replace(/\//g, "_").toUpperCase();
|
|
166
|
+
const envKey = process.env[`PSPM_ENCRYPTION_KEY_${envSuffix}`];
|
|
167
|
+
if (envKey) return envKey;
|
|
168
|
+
return (await readUserConfig()).encryptionKeys?.[scope];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Set the encryption key for a given scope in ~/.pspmrc.
|
|
172
|
+
*
|
|
173
|
+
* @param scope - The scope (e.g., "@user/alice" or "@org/acme")
|
|
174
|
+
* @param key - The encryption passphrase
|
|
175
|
+
*/
|
|
176
|
+
async function setEncryptionKey(scope, key) {
|
|
177
|
+
const config = await readUserConfig();
|
|
178
|
+
if (!config.encryptionKeys) config.encryptionKeys = {};
|
|
179
|
+
config.encryptionKeys[scope] = key;
|
|
180
|
+
await writeUserConfig(config);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Remove the encryption key for a given scope from ~/.pspmrc.
|
|
184
|
+
*
|
|
185
|
+
* @param scope - The scope (e.g., "@user/alice" or "@org/acme")
|
|
186
|
+
*/
|
|
187
|
+
async function removeEncryptionKey(scope) {
|
|
188
|
+
const config = await readUserConfig();
|
|
189
|
+
if (config.encryptionKeys) {
|
|
190
|
+
delete config.encryptionKeys[scope];
|
|
191
|
+
if (Object.keys(config.encryptionKeys).length === 0) config.encryptionKeys = void 0;
|
|
192
|
+
}
|
|
193
|
+
await writeUserConfig(config);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Derive the encryption scope from a package specifier.
|
|
197
|
+
*
|
|
198
|
+
* @user/alice/my-skill -> @user/alice
|
|
199
|
+
* @org/acme/tool -> @org/acme
|
|
200
|
+
*/
|
|
201
|
+
function getEncryptionScope(namespace, owner) {
|
|
202
|
+
return `@${namespace}/${owner}`;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/config.ts
|
|
206
|
+
const DEFAULT_REGISTRY_URL = "https://registry.pspm.dev";
|
|
207
|
+
/**
|
|
208
|
+
* Get the user config file path (~/.pspmrc)
|
|
209
|
+
*/
|
|
210
|
+
function getConfigPath() {
|
|
211
|
+
return join(homedir(), ".pspmrc");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get the legacy config file path (~/.pspm/config.json) for migration
|
|
215
|
+
*/
|
|
216
|
+
function getLegacyConfigPath() {
|
|
217
|
+
return join(homedir(), ".pspm", "config.json");
|
|
218
|
+
}
|
|
219
|
+
let _globalMode = false;
|
|
220
|
+
/**
|
|
221
|
+
* Set global mode. When enabled, all path functions return
|
|
222
|
+
* home-directory paths (~/.pspm/) instead of project-relative paths.
|
|
223
|
+
*/
|
|
224
|
+
function setGlobalMode(global) {
|
|
225
|
+
_globalMode = global;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check if global mode is enabled.
|
|
229
|
+
*/
|
|
230
|
+
function isGlobalMode() {
|
|
231
|
+
return _globalMode;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get the .pspm directory path
|
|
235
|
+
* Global: ~/.pspm/
|
|
236
|
+
* Project: ./.pspm/
|
|
237
|
+
*/
|
|
238
|
+
function getPspmDir() {
|
|
239
|
+
if (_globalMode) return join(homedir(), ".pspm");
|
|
240
|
+
return join(process.cwd(), ".pspm");
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the skills directory path
|
|
244
|
+
* Global: ~/.pspm/skills/
|
|
245
|
+
* Project: ./.pspm/skills/
|
|
246
|
+
*/
|
|
247
|
+
function getSkillsDir() {
|
|
248
|
+
return join(getPspmDir(), "skills");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the cache directory path
|
|
252
|
+
* Global: ~/.pspm/cache/
|
|
253
|
+
* Project: ./.pspm/cache/
|
|
254
|
+
*/
|
|
255
|
+
function getCacheDir() {
|
|
256
|
+
return join(getPspmDir(), "cache");
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Get the lockfile path
|
|
260
|
+
* Global: ~/.pspm/pspm-lock.json
|
|
261
|
+
* Project: ./pspm-lock.json
|
|
262
|
+
*/
|
|
263
|
+
function getLockfilePath() {
|
|
264
|
+
if (_globalMode) return join(homedir(), ".pspm", "pspm-lock.json");
|
|
265
|
+
return join(process.cwd(), "pspm-lock.json");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get the legacy lockfile path (for migration)
|
|
269
|
+
*/
|
|
270
|
+
function getLegacyLockfilePath() {
|
|
271
|
+
return join(process.cwd(), "skill-lock.json");
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get the legacy skills directory path (for migration)
|
|
275
|
+
*/
|
|
276
|
+
function getLegacySkillsDir() {
|
|
277
|
+
return join(process.cwd(), ".skills");
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Read the user config file (~/.pspmrc, INI format)
|
|
281
|
+
*
|
|
282
|
+
* Supports npm-style configuration:
|
|
283
|
+
* ```ini
|
|
284
|
+
* ; Default registry and auth
|
|
285
|
+
* registry = https://pspm.dev
|
|
286
|
+
* authToken = sk_default
|
|
287
|
+
*
|
|
288
|
+
* ; Scope mappings
|
|
289
|
+
* @myorg:registry = https://corp.pspm.io
|
|
290
|
+
*
|
|
291
|
+
* ; Per-registry tokens
|
|
292
|
+
* //pspm.dev:authToken = sk_public
|
|
293
|
+
* //corp.pspm.io:authToken = sk_corp
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
async function readUserConfig() {
|
|
297
|
+
const configPath = getConfigPath();
|
|
298
|
+
if (process.env.PSPM_DEBUG) console.log(`[config] Reading config from: ${configPath}`);
|
|
299
|
+
try {
|
|
300
|
+
const content = await readFile(configPath, "utf-8");
|
|
301
|
+
const parsed = ini.parse(content);
|
|
302
|
+
if (process.env.PSPM_DEBUG) console.log("[config] Parsed config:", JSON.stringify(parsed, null, 2));
|
|
303
|
+
const scopedRegistries = {};
|
|
304
|
+
for (const key of Object.keys(parsed)) {
|
|
305
|
+
const scopeMatch = key.match(/^(@[^:]+):registry$/);
|
|
306
|
+
if (scopeMatch) {
|
|
307
|
+
const scope = scopeMatch[1];
|
|
308
|
+
scopedRegistries[scope] = parsed[key];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const registryTokens = {};
|
|
312
|
+
for (const key of Object.keys(parsed)) {
|
|
313
|
+
const tokenMatch = key.match(/^\/\/([^:]+):authToken$/);
|
|
314
|
+
if (tokenMatch) {
|
|
315
|
+
const host = tokenMatch[1];
|
|
316
|
+
registryTokens[host] = parsed[key];
|
|
317
|
+
}
|
|
318
|
+
if (key.startsWith("//") && typeof parsed[key] === "object") {
|
|
319
|
+
const host = key.slice(2);
|
|
320
|
+
const section = parsed[key];
|
|
321
|
+
if (section.authToken) registryTokens[host] = section.authToken;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const encryptionKeys = {};
|
|
325
|
+
for (const key of Object.keys(parsed)) {
|
|
326
|
+
const encKeyMatch = key.match(/^encryption-key:(.+)$/);
|
|
327
|
+
if (encKeyMatch) {
|
|
328
|
+
const scope = encKeyMatch[1];
|
|
329
|
+
encryptionKeys[scope] = parsed[key];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
registry: parsed.registry,
|
|
334
|
+
authToken: parsed.authToken,
|
|
335
|
+
username: parsed.username,
|
|
336
|
+
scopedRegistries: Object.keys(scopedRegistries).length > 0 ? scopedRegistries : void 0,
|
|
337
|
+
registryTokens: Object.keys(registryTokens).length > 0 ? registryTokens : void 0,
|
|
338
|
+
encryptionKeys: Object.keys(encryptionKeys).length > 0 ? encryptionKeys : void 0
|
|
339
|
+
};
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (process.env.PSPM_DEBUG) console.log(`[config] Error reading config: ${error instanceof Error ? error.message : String(error)}`);
|
|
342
|
+
return {};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Write the user config file (~/.pspmrc, INI format)
|
|
347
|
+
*/
|
|
348
|
+
async function writeUserConfig(config) {
|
|
349
|
+
const configPath = getConfigPath();
|
|
350
|
+
const lines = ["; PSPM Configuration", ""];
|
|
351
|
+
if (config.registry) lines.push(`registry = ${config.registry}`);
|
|
352
|
+
if (config.authToken) lines.push(`authToken = ${config.authToken}`);
|
|
353
|
+
if (config.username) lines.push(`username = ${config.username}`);
|
|
354
|
+
if (config.encryptionKeys && Object.keys(config.encryptionKeys).length > 0) {
|
|
355
|
+
lines.push("; Encryption keys (scope -> passphrase)");
|
|
356
|
+
for (const [scope, key] of Object.entries(config.encryptionKeys)) lines.push(`encryption-key:${scope} = ${key}`);
|
|
357
|
+
}
|
|
358
|
+
lines.push("");
|
|
359
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
360
|
+
await writeFile(configPath, lines.join("\n"));
|
|
361
|
+
if (process.env.PSPM_DEBUG) console.log(`[config] Wrote config to: ${configPath}`);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Find and read project config (.pspmrc) by searching up directory tree
|
|
365
|
+
*/
|
|
366
|
+
async function findProjectConfig() {
|
|
367
|
+
let currentDir = process.cwd();
|
|
368
|
+
const root = dirname(currentDir);
|
|
369
|
+
while (currentDir !== root) {
|
|
370
|
+
const configPath = join(currentDir, ".pspmrc");
|
|
371
|
+
try {
|
|
372
|
+
if ((await stat(configPath)).isFile()) {
|
|
373
|
+
const content = await readFile(configPath, "utf-8");
|
|
374
|
+
try {
|
|
375
|
+
const parsed = ini.parse(content);
|
|
376
|
+
if (process.env.PSPM_DEBUG) console.log(`[config] Found project config at ${configPath}:`, JSON.stringify(parsed, null, 2));
|
|
377
|
+
return { registry: parsed.registry };
|
|
378
|
+
} catch {
|
|
379
|
+
try {
|
|
380
|
+
return { registry: JSON.parse(content).registryUrl };
|
|
381
|
+
} catch {}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} catch {}
|
|
385
|
+
currentDir = dirname(currentDir);
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Migrate from legacy config format (~/.pspm/config.json) if it exists
|
|
391
|
+
*/
|
|
392
|
+
async function migrateFromLegacyConfig() {
|
|
393
|
+
const legacyPath = getLegacyConfigPath();
|
|
394
|
+
try {
|
|
395
|
+
const content = await readFile(legacyPath, "utf-8");
|
|
396
|
+
const parsed = JSON.parse(content);
|
|
397
|
+
let config = {};
|
|
398
|
+
if (parsed.version === 2 && parsed.profiles) {
|
|
399
|
+
const v2Config = parsed;
|
|
400
|
+
const defaultProfileName = v2Config.defaultProfile || "default";
|
|
401
|
+
const profile = v2Config.profiles[defaultProfileName];
|
|
402
|
+
if (profile) config = {
|
|
403
|
+
registry: profile.registryUrl !== "https://registry.pspm.dev" ? profile.registryUrl : void 0,
|
|
404
|
+
authToken: profile.apiKey,
|
|
405
|
+
username: profile.username
|
|
406
|
+
};
|
|
407
|
+
console.log(`Migrating from legacy config (profile: ${defaultProfileName})...`);
|
|
408
|
+
} else {
|
|
409
|
+
const v1Config = parsed;
|
|
410
|
+
config = {
|
|
411
|
+
registry: v1Config.registryUrl !== "https://registry.pspm.dev" ? v1Config.registryUrl : void 0,
|
|
412
|
+
authToken: v1Config.apiKey,
|
|
413
|
+
username: v1Config.username
|
|
414
|
+
};
|
|
415
|
+
console.log("Migrating from legacy config...");
|
|
416
|
+
}
|
|
417
|
+
await writeUserConfig(config);
|
|
418
|
+
console.log(`Created new config at: ${getConfigPath()}`);
|
|
419
|
+
await unlink(legacyPath);
|
|
420
|
+
console.log(`Removed legacy config: ${legacyPath}`);
|
|
421
|
+
return config;
|
|
422
|
+
} catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Resolve the full configuration using cascade priority:
|
|
428
|
+
* 1. Environment variables (PSPM_REGISTRY_URL, PSPM_API_KEY)
|
|
429
|
+
* 2. Project config (.pspmrc in project directory)
|
|
430
|
+
* 3. User config (~/.pspmrc)
|
|
431
|
+
* 4. Defaults
|
|
432
|
+
*/
|
|
433
|
+
async function resolveConfig() {
|
|
434
|
+
const newConfigPath = getConfigPath();
|
|
435
|
+
try {
|
|
436
|
+
await stat(newConfigPath);
|
|
437
|
+
} catch {
|
|
438
|
+
await migrateFromLegacyConfig();
|
|
439
|
+
}
|
|
440
|
+
const userConfig = await readUserConfig();
|
|
441
|
+
const projectConfig = await findProjectConfig();
|
|
442
|
+
let registryUrl = DEFAULT_REGISTRY_URL;
|
|
443
|
+
let apiKey = userConfig.authToken;
|
|
444
|
+
const username = userConfig.username;
|
|
445
|
+
const scopedRegistries = userConfig.scopedRegistries ?? {};
|
|
446
|
+
const registryTokens = userConfig.registryTokens ?? {};
|
|
447
|
+
if (userConfig.registry) registryUrl = userConfig.registry;
|
|
448
|
+
if (projectConfig?.registry) registryUrl = projectConfig.registry;
|
|
449
|
+
if (process.env.PSPM_REGISTRY_URL) registryUrl = process.env.PSPM_REGISTRY_URL;
|
|
450
|
+
if (process.env.PSPM_API_KEY) apiKey = process.env.PSPM_API_KEY;
|
|
451
|
+
if (process.env.PSPM_DEBUG) {
|
|
452
|
+
console.log("[config] Resolved config:");
|
|
453
|
+
console.log(`[config] registryUrl: ${registryUrl}`);
|
|
454
|
+
console.log(`[config] apiKey: ${apiKey ? "***" : "(not set)"}`);
|
|
455
|
+
console.log(`[config] username: ${username || "(not set)"}`);
|
|
456
|
+
console.log(`[config] scopedRegistries: ${JSON.stringify(scopedRegistries)}`);
|
|
457
|
+
console.log(`[config] registryTokens: ${Object.keys(registryTokens).length} configured`);
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
registryUrl,
|
|
461
|
+
apiKey,
|
|
462
|
+
username,
|
|
463
|
+
scopedRegistries,
|
|
464
|
+
registryTokens
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
//#endregion
|
|
468
|
+
export { setCredentials as C, requireApiKey as S, extractApiErrorMessage as T, getEncryptionScope as _, getLegacyLockfilePath as a, isLoggedIn as b, getPspmDir as c, readUserConfig as d, resolveConfig as f, getEncryptionKey as g, clearCredentials as h, getConfigPath as i, getSkillsDir as l, writeUserConfig as m, findProjectConfig as n, getLegacySkillsDir as o, setGlobalMode as p, getCacheDir as r, getLockfilePath as s, DEFAULT_REGISTRY_URL as t, isGlobalMode as u, getRegistryUrl as v, setEncryptionKey as w, removeEncryptionKey as x, getTokenForRegistry as y };
|