Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,22 +276,26 @@ function printKeyBox(apiKey: string, storageLocation: string): void {
console.log(` └${"─".repeat(innerWidth + 2)}┘`);
}

function saveApiConfig(rawApiUrl: string): void {
function saveApiConfig(rawApiUrl: string, apiKey?: string): void {
try {
const nextConfig = { ...loadConfig(), api_url: rawApiUrl };
delete nextConfig.api_key;
if (apiKey) {
nextConfig.api_key = apiKey;
} else {
delete nextConfig.api_key;
}
saveConfig(nextConfig);
} catch (error) {
throw new UserFacingError(`Unable to save config: ${extractMessage(error)}`);
}
}

function storeApiKey(apiKey: string): string {
function storeApiKey(apiKey: string): { location: string; secure: boolean } {
try {
return persistApiKey(apiKey);
} catch (error) {
throw new UserFacingError(
`Unable to store API key securely: ${extractMessage(error)}`,
`Unable to store API key: ${extractMessage(error)}`,
);
}
}
Expand Down Expand Up @@ -456,10 +460,10 @@ async function finalizeSetup(rawApiUrl: string, apiKey: string, dashboardUrl: st
// Validate API connectivity before persisting anything to disk.
// If this fails, no config or key is saved — the user can retry cleanly.
const task = await createDemoTask(apiBaseUrl, apiKey);
const storageLocation = storeApiKey(apiKey);
saveApiConfig(rawApiUrl);
const storage = storeApiKey(apiKey);
saveApiConfig(rawApiUrl, storage.secure ? undefined : apiKey);

return { apiKey, apiUrl: rawApiUrl, storageLocation, task, dashboardUrl };
return { apiKey, apiUrl: rawApiUrl, storageLocation: storage.location, task, dashboardUrl };
} catch (error) {
// The account/agent already exists server-side — print the key so it isn't lost.
console.error();
Expand Down
17 changes: 11 additions & 6 deletions src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,21 @@ Examples:

let storageLocation: string;
try {
storageLocation = persistApiKey(key);
const storage = persistApiKey(key);
storageLocation = storage.location;

const nextConfig = { ...config };
if (storage.secure) {
delete nextConfig.api_key;
} else {
nextConfig.api_key = key;
}
saveConfig(nextConfig);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error(`Unable to store API key securely: ${msg}`);
console.error(`Unable to store API key: ${msg}`);
process.exit(1);
Comment on lines 168 to 171
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Mixed error message if config write fails

saveConfig is now inside the catch-all try block, which is the right change. But if saveConfig throws (e.g. a permission error writing the config directory), the catch clause will surface it as Unable to store API key, implying the credential-store step failed rather than the file write.

For clarity, consider wrapping saveConfig in its own try/catch with a distinct message like Unable to save config, consistent with the wording already used in saveApiConfig inside init.ts.

}

const nextConfig = { ...config };
delete nextConfig.api_key;
saveConfig(nextConfig);
if (validatedWithoutMetadata) {
console.log(`\nLogged in. Key saved to ${storageLocation}`);
console.log("Current server validated the key but does not expose /agent/me metadata.");
Expand Down
19 changes: 14 additions & 5 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export interface DelegaConfig {
api_url?: string;
}

export interface ApiKeyStorageResult {
location: string;
secure: boolean;
}

const LOCAL_API_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);

function normalizeHost(hostname: string): string {
Expand Down Expand Up @@ -60,15 +65,19 @@ export function saveConfig(config: DelegaConfig): void {
node_fs.chmodSync(getConfigPath(), 0o600);
}

export function persistApiKey(apiKey: string): string {
export function persistApiKey(apiKey: string): ApiKeyStorageResult {
const storeLabel = storeApiKey(apiKey);
if (storeLabel) {
return storeLabel;
return {
location: storeLabel,
secure: true,
};
}

throw new Error(
"Secure credential storage is unavailable on this system. Set DELEGA_API_KEY manually instead.",
);
return {
location: getConfigPath(),
secure: false,
};
}

export function normalizeApiUrl(rawUrl: string): string {
Expand Down
9 changes: 7 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ program.addCommand(statsCommand);
program.addCommand(statusCommand);
program.addCommand(resetCommand);

program.on("command:*", () => {
program.on("command:*", ([commandName]) => {
printBanner();
program.help();
if (commandName) {
console.error(`Unknown command: ${commandName}`);
console.error();
}
program.outputHelp();
process.exit(1);
});

if (process.argv.length <= 2) {
Expand Down