careervivid 1.12.25 ā 1.12.27
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/dist/commands/agent/configurator.d.ts.map +1 -1
- package/dist/commands/agent/configurator.js +46 -13
- package/dist/commands/agent/repl.d.ts.map +1 -1
- package/dist/commands/agent/repl.js +65 -5
- package/dist/config.d.ts +7 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configurator.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/configurator.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"configurator.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/configurator.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0D,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI3G,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKpD,CAAC;AAEF,eAAO,MAAM,SAAS;;;;;GAyBrB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;GA+BtB,CAAC;AAEF,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAsEpD;AAED,wBAAsB,mBAAmB,CAAC,OAAO,GAAE,GAAQ,GAAG,OAAO,CAAC;IACpE,gBAAgB,EAAE,WAAW,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAiKD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import pkg from "enquirer";
|
|
3
|
-
import { loadConfig, saveConfig } from "../../config.js";
|
|
3
|
+
import { loadConfig, saveConfig, setProviderKey, getProviderKey } from "../../config.js";
|
|
4
4
|
const { prompt } = pkg;
|
|
5
5
|
export const MODEL_CREDIT_COST = {
|
|
6
6
|
"gemini-3.1-flash-lite-preview": 0.5,
|
|
@@ -165,20 +165,52 @@ export async function promptForAgentModel(options = {}) {
|
|
|
165
165
|
__custom__: "custom",
|
|
166
166
|
};
|
|
167
167
|
selectedProvider = providerMap[picked] || "openai";
|
|
168
|
-
const
|
|
169
|
-
|
|
168
|
+
const providerLabels = {
|
|
169
|
+
openai: "OpenAI",
|
|
170
|
+
anthropic: "Anthropic",
|
|
171
|
+
gemini: "Gemini (Google AI Studio)",
|
|
172
|
+
openrouter: "OpenRouter",
|
|
173
|
+
custom: "Custom endpoint",
|
|
174
|
+
};
|
|
175
|
+
const providerKeyUrls = {
|
|
176
|
+
openai: "https://platform.openai.com/api-keys",
|
|
177
|
+
anthropic: "https://console.anthropic.com/settings/keys",
|
|
178
|
+
gemini: "https://aistudio.google.com/app/apikey",
|
|
179
|
+
openrouter: "https://openrouter.ai/settings/keys",
|
|
180
|
+
custom: "your custom endpoint's documentation",
|
|
181
|
+
};
|
|
182
|
+
// Check for this specific provider's saved key ā NOT a shared key
|
|
183
|
+
const savedKey = getProviderKey(selectedProvider);
|
|
170
184
|
if (!savedKey) {
|
|
171
|
-
console.log(
|
|
172
|
-
console.log(chalk.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
console.log();
|
|
186
|
+
console.log(chalk.bold.yellow(`š API key required for ${providerLabels[selectedProvider] ?? selectedProvider}`) +
|
|
187
|
+
chalk.dim("\n Your key will be saved locally ā you only need to enter it once."));
|
|
188
|
+
console.log(chalk.dim(`\n Get your key at: `) + chalk.cyan(providerKeyUrls[selectedProvider] ?? "the provider's website"));
|
|
189
|
+
console.log();
|
|
190
|
+
try {
|
|
191
|
+
const keyAnswer = await prompt({
|
|
192
|
+
type: "password",
|
|
193
|
+
name: "key",
|
|
194
|
+
message: `Enter your ${providerLabels[selectedProvider] ?? selectedProvider} API key:`,
|
|
195
|
+
});
|
|
196
|
+
apiKey = (keyAnswer?.key ?? "").trim();
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// User pressed Escape or Ctrl+C ā exit cleanly
|
|
200
|
+
console.log(chalk.dim("\n Cancelled. Run `cv agent` again when you have your API key.\n"));
|
|
201
|
+
process.exit(0);
|
|
202
|
+
}
|
|
203
|
+
if (apiKey) {
|
|
204
|
+
setProviderKey(selectedProvider, apiKey);
|
|
205
|
+
console.log(chalk.green(`\nā Key saved for ${providerLabels[selectedProvider] ?? selectedProvider}\n`));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log(chalk.yellow("\nā ļø No key entered. Continuing without API key ā the agent may fail.\n"));
|
|
209
|
+
}
|
|
179
210
|
}
|
|
180
|
-
|
|
181
|
-
apiKey = savedKey;
|
|
211
|
+
else {
|
|
212
|
+
apiKey = savedKey;
|
|
213
|
+
console.log(chalk.dim(`\n Using saved ${providerLabels[selectedProvider] ?? selectedProvider} key (ā¢ā¢ā¢ā¢${savedKey.slice(-4)})\n`));
|
|
182
214
|
}
|
|
183
215
|
// Attempt to automatically fetch and display Free OpenRouter models that support tools
|
|
184
216
|
if (selectedProvider === "openrouter") {
|
|
@@ -217,6 +249,7 @@ export async function promptForAgentModel(options = {}) {
|
|
|
217
249
|
}
|
|
218
250
|
}
|
|
219
251
|
// Pre-fill with the saved model when provider matches, otherwise use sensible defaults
|
|
252
|
+
const savedCfg = loadConfig();
|
|
220
253
|
const defaultModel = savedCfg.llmProvider === selectedProvider && savedCfg.llmModel
|
|
221
254
|
? savedCfg.llmModel
|
|
222
255
|
: selectedProvider === "openai"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,
|
|
1
|
+
{"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../../src/commands/agent/repl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAA4D,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI7G,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,MAAM,GAAG,IAAW,QAwBtF;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,WAAW,GAAG,sBAAsB,GAAG,IAAI,EACnD,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9K,gBAAgB,EAAE,WAAW,EAC7B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,GAAG,EAAE,GACX,OAAO,CAAC,IAAI,CAAC,CA4ff"}
|
|
@@ -4,7 +4,7 @@ import ora from "ora";
|
|
|
4
4
|
import { isSafeCommand } from "../../agent/tools/coding.js";
|
|
5
5
|
import { CareerVividProxyEngine } from "../../agent/CareerVividProxyEngine.js";
|
|
6
6
|
import { CV_MODELS } from "./configurator.js";
|
|
7
|
-
import { loadConfig,
|
|
7
|
+
import { loadConfig, getProviderKey, setProviderKey } from "../../config.js";
|
|
8
8
|
const { prompt } = pkg;
|
|
9
9
|
export function printCreditStatus(remaining, limit = null) {
|
|
10
10
|
if (remaining === null)
|
|
@@ -366,8 +366,8 @@ ${label}
|
|
|
366
366
|
sessionTurns++;
|
|
367
367
|
const { createOpenAICompatibleProvider } = await import("../../agent/providers/OpenAIProvider.js");
|
|
368
368
|
const { AnthropicProvider } = await import("../../agent/providers/AnthropicProvider.js");
|
|
369
|
-
const byoApiKey = options["api-key"] || loadConfig().llmApiKey;
|
|
370
|
-
const key = byoApiKey ||
|
|
369
|
+
const byoApiKey = options["api-key"] || getProviderKey(selectedProvider) || loadConfig().llmApiKey;
|
|
370
|
+
const key = byoApiKey || "";
|
|
371
371
|
const baseUrl = options["base-url"] || loadConfig().llmBaseUrl;
|
|
372
372
|
let provider;
|
|
373
373
|
if (selectedProvider === "anthropic") {
|
|
@@ -425,11 +425,71 @@ ${label}
|
|
|
425
425
|
return ask();
|
|
426
426
|
}
|
|
427
427
|
catch (err) {
|
|
428
|
-
|
|
428
|
+
const msg = err?.message ?? "";
|
|
429
|
+
// āā Clean exit on Ctrl+C / enquirer cancel āāāāāāāāāāāāāāāāāāāāāāāā
|
|
430
|
+
if (!msg || msg.includes("cancelled") || msg.includes("User force closed")) {
|
|
429
431
|
console.log(chalk.gray("\nCancelled. Exiting.\n"));
|
|
430
432
|
process.exit(0);
|
|
431
433
|
}
|
|
432
|
-
|
|
434
|
+
// āā 401 unauthorized ā offer key reset āāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
435
|
+
const is401 = msg.includes("401") || msg.toLowerCase().includes("user not found") ||
|
|
436
|
+
msg.toLowerCase().includes("invalid api key") || msg.toLowerCase().includes("unauthorized");
|
|
437
|
+
if (is401 && selectedProvider && selectedProvider !== "careervivid") {
|
|
438
|
+
const providerLabels = {
|
|
439
|
+
openai: "OpenAI", anthropic: "Anthropic",
|
|
440
|
+
gemini: "Gemini", openrouter: "OpenRouter", custom: "Custom",
|
|
441
|
+
};
|
|
442
|
+
const providerKeyUrls = {
|
|
443
|
+
openai: "https://platform.openai.com/api-keys",
|
|
444
|
+
anthropic: "https://console.anthropic.com/settings/keys",
|
|
445
|
+
gemini: "https://aistudio.google.com/app/apikey",
|
|
446
|
+
openrouter: "https://openrouter.ai/settings/keys",
|
|
447
|
+
};
|
|
448
|
+
const label = providerLabels[selectedProvider] ?? selectedProvider;
|
|
449
|
+
console.log();
|
|
450
|
+
console.log(chalk.red(`ā API key rejected by ${label} (401 Unauthorized).`));
|
|
451
|
+
console.log(chalk.dim(` The saved key may be expired or invalid.`));
|
|
452
|
+
if (providerKeyUrls[selectedProvider]) {
|
|
453
|
+
console.log(chalk.dim(` Get a new key at: `) + chalk.cyan(providerKeyUrls[selectedProvider]));
|
|
454
|
+
}
|
|
455
|
+
console.log();
|
|
456
|
+
try {
|
|
457
|
+
const resetAnswer = await prompt({
|
|
458
|
+
type: "select",
|
|
459
|
+
name: "action",
|
|
460
|
+
message: "What would you like to do?",
|
|
461
|
+
choices: [
|
|
462
|
+
{ name: "reset", message: `š Enter a new ${label} API key` },
|
|
463
|
+
{ name: "continue", message: "āļø Continue anyway (will keep failing)" },
|
|
464
|
+
{ name: "exit", message: "šŖ Exit the agent" },
|
|
465
|
+
],
|
|
466
|
+
});
|
|
467
|
+
if (resetAnswer.action === "reset") {
|
|
468
|
+
const keyAnswer = await prompt({
|
|
469
|
+
type: "password",
|
|
470
|
+
name: "key",
|
|
471
|
+
message: `Enter your new ${label} API key:`,
|
|
472
|
+
});
|
|
473
|
+
const newKey = (keyAnswer?.key ?? "").trim();
|
|
474
|
+
if (newKey) {
|
|
475
|
+
setProviderKey(selectedProvider, newKey);
|
|
476
|
+
// Update the key used for subsequent turns this session
|
|
477
|
+
options["api-key"] = newKey;
|
|
478
|
+
console.log(chalk.green(`\nā New ${label} key saved. Resuming session...\n`));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (resetAnswer.action === "exit") {
|
|
482
|
+
console.log(chalk.gray("\nGoodbye! š\n"));
|
|
483
|
+
process.exit(0);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
// User cancelled the reset prompt ā just continue
|
|
488
|
+
}
|
|
489
|
+
return ask();
|
|
490
|
+
}
|
|
491
|
+
// āā Generic error āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
492
|
+
console.error(chalk.red(`\nAgent encountered an error: ${msg}`));
|
|
433
493
|
return ask();
|
|
434
494
|
}
|
|
435
495
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -20,8 +20,10 @@ export interface CareerVividConfig {
|
|
|
20
20
|
llmProvider?: LLMProvider;
|
|
21
21
|
/** BYO model identifier (e.g. "gpt-4o", "claude-opus-4-5", "gemini-2.5-pro") */
|
|
22
22
|
llmModel?: string;
|
|
23
|
-
/**
|
|
23
|
+
/** @deprecated use llmKeys[provider] instead ā kept for migration */
|
|
24
24
|
llmApiKey?: string;
|
|
25
|
+
/** Per-provider API keys ā keyed by provider name */
|
|
26
|
+
llmKeys?: Partial<Record<LLMProvider, string>>;
|
|
25
27
|
/** Custom base URL for OpenAI-compatible endpoints (OpenRouter, Kimi, GLM, Qwen, etc.) */
|
|
26
28
|
llmBaseUrl?: string;
|
|
27
29
|
}
|
|
@@ -36,6 +38,10 @@ export declare function saveConfig(config: CareerVividConfig): void;
|
|
|
36
38
|
export declare function getApiKey(): string | undefined;
|
|
37
39
|
/** Gemini API key used by `cv agent`. Priority: GEMINI_API_KEY env var > geminiKey in config. */
|
|
38
40
|
export declare function getGeminiKey(): string | undefined;
|
|
41
|
+
/** Get the saved API key for a specific BYO provider */
|
|
42
|
+
export declare function getProviderKey(provider: LLMProvider): string | undefined;
|
|
43
|
+
/** Save the API key for a specific BYO provider */
|
|
44
|
+
export declare function setProviderKey(provider: LLMProvider, key: string): void;
|
|
39
45
|
export declare function getApiUrl(): string;
|
|
40
46
|
export declare function setConfigValue(key: keyof CareerVividConfig, value: string): void;
|
|
41
47
|
/** How long a CLI session stays valid (90 days in ms) */
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,eAAO,MAAM,WAAW,QAAyC,CAAC;AAElE,eAAO,MAAM,eAAe,gCAAgC,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEtG,MAAM,WAAW,iBAAiB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,eAAO,MAAM,WAAW,QAAyC,CAAC;AAElE,eAAO,MAAM,eAAe,gCAAgC,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEtG,MAAM,WAAW,iBAAiB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACtB,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,IAAI,iBAAiB,CAQ9C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAE1D;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED,iGAAiG;AACjG,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAIxE;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQvE;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,iBAAiB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIhF;AAID,yDAAyD;AACzD,eAAO,MAAM,cAAc,QAA2B,CAAC;AAEvD,wDAAwD;AACxD,wBAAgB,cAAc,IAAI,OAAO,CAMxC;AAED,mFAAmF;AACnF,wBAAgB,YAAY,IAAI,IAAI,CAInC;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE;IACrC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,SAAS,CA6BZ"}
|
package/dist/config.js
CHANGED
|
@@ -32,6 +32,22 @@ export function getApiKey() {
|
|
|
32
32
|
export function getGeminiKey() {
|
|
33
33
|
return process.env.GEMINI_API_KEY || loadConfig().geminiKey;
|
|
34
34
|
}
|
|
35
|
+
/** Get the saved API key for a specific BYO provider */
|
|
36
|
+
export function getProviderKey(provider) {
|
|
37
|
+
const cfg = loadConfig();
|
|
38
|
+
// Per-provider key takes priority, fallback to old llmApiKey (migration)
|
|
39
|
+
return cfg.llmKeys?.[provider] ?? (cfg.llmProvider === provider ? cfg.llmApiKey : undefined);
|
|
40
|
+
}
|
|
41
|
+
/** Save the API key for a specific BYO provider */
|
|
42
|
+
export function setProviderKey(provider, key) {
|
|
43
|
+
const cfg = loadConfig();
|
|
44
|
+
cfg.llmKeys = cfg.llmKeys ?? {};
|
|
45
|
+
cfg.llmKeys[provider] = key;
|
|
46
|
+
// Also write to llmApiKey for backwards compat when this is the active provider
|
|
47
|
+
cfg.llmApiKey = key;
|
|
48
|
+
cfg.llmProvider = provider;
|
|
49
|
+
saveConfig(cfg);
|
|
50
|
+
}
|
|
35
51
|
export function getApiUrl() {
|
|
36
52
|
return process.env.CV_API_URL || loadConfig().apiUrl || DEFAULT_API_URL;
|
|
37
53
|
}
|
|
@@ -83,7 +99,7 @@ export function getLlmConfig(overrides) {
|
|
|
83
99
|
'gemini-2.5-flash';
|
|
84
100
|
const apiKey = overrides?.apiKey ??
|
|
85
101
|
process.env.CV_LLM_API_KEY ??
|
|
86
|
-
|
|
102
|
+
getProviderKey(provider) ??
|
|
87
103
|
cfg.geminiKey ??
|
|
88
104
|
process.env.GEMINI_API_KEY;
|
|
89
105
|
const baseUrl = overrides?.baseUrl ??
|
package/package.json
CHANGED