Vendored sashidown, OpenAI-compatible env (OPENAI_*), and bootstrap scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
141 lines
3.4 KiB
C
141 lines
3.4 KiB
C
#define _DEFAULT_SOURCE
|
||
|
||
#include "cst_agent.h"
|
||
#include "os_compat.h"
|
||
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#ifdef _WIN32
|
||
#include <io.h>
|
||
#else
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
/* Config dir via os_compat: ~/.config/cst-agent or %APPDATA%\cst-agent */
|
||
|
||
static int credentials_path(char *out, size_t n) {
|
||
char dir[OS_PATH_MAX];
|
||
if (os_config_dir(dir, sizeof(dir)) != 0)
|
||
return -1;
|
||
return os_path_join(out, n, dir, "credentials");
|
||
}
|
||
|
||
int cst_creds_load_api_key(char *out, size_t out_sz) {
|
||
if (!out || out_sz < 2)
|
||
return -1;
|
||
out[0] = 0;
|
||
char path[OS_PATH_MAX];
|
||
if (credentials_path(path, sizeof(path)) != 0)
|
||
return -1;
|
||
FILE *fp = fopen(path, "r");
|
||
if (!fp)
|
||
return -1;
|
||
char line[1024];
|
||
while (fgets(line, sizeof(line), fp)) {
|
||
if (strncmp(line, "api_key=", 8) != 0)
|
||
continue;
|
||
char *v = line + 8;
|
||
size_t len = strlen(v);
|
||
while (len > 0 && (v[len - 1] == '\n' || v[len - 1] == '\r'))
|
||
v[--len] = 0;
|
||
if (len == 0 || len + 1 > out_sz) {
|
||
fclose(fp);
|
||
return -1;
|
||
}
|
||
memcpy(out, v, len + 1);
|
||
fclose(fp);
|
||
return 0;
|
||
}
|
||
fclose(fp);
|
||
return -1;
|
||
}
|
||
|
||
int cst_creds_save_api_key(const char *key) {
|
||
if (!key || !key[0])
|
||
return -1;
|
||
char dir[OS_PATH_MAX], path[OS_PATH_MAX];
|
||
if (os_config_dir(dir, sizeof(dir)) != 0)
|
||
return -1;
|
||
if (os_mkdir(dir, 0700) != 0)
|
||
return -1;
|
||
if (credentials_path(path, sizeof(path)) != 0)
|
||
return -1;
|
||
FILE *fp = fopen(path, "w");
|
||
if (!fp)
|
||
return -1;
|
||
fprintf(fp, "api_key=%s\n", key);
|
||
fprintf(fp, "api_base=https://api.minimax.cn/v1\n");
|
||
fprintf(fp, "model=MiniMax-M2.7\n");
|
||
fclose(fp);
|
||
(void)os_chmod_private(path);
|
||
return 0;
|
||
}
|
||
|
||
/* Zenity on Unix; stdin prompt elsewhere / when zenity missing. */
|
||
int cst_creds_prompt_api_key(char *out, size_t out_sz) {
|
||
if (!out || out_sz < 2)
|
||
return -1;
|
||
out[0] = 0;
|
||
|
||
#ifndef _WIN32
|
||
FILE *fp = popen(
|
||
"zenity --password --title='CST-Agent / MiniMax' "
|
||
"--text='请输入 MiniMax API Key(将保存到配置目录 credentials)' "
|
||
"2>/dev/null",
|
||
"r");
|
||
if (fp) {
|
||
if (fgets(out, (int)out_sz, fp)) {
|
||
int st = pclose(fp);
|
||
size_t len = strlen(out);
|
||
while (len > 0 && (out[len - 1] == '\n' || out[len - 1] == '\r'))
|
||
out[--len] = 0;
|
||
if (st == 0 && len > 0)
|
||
return 0;
|
||
} else {
|
||
pclose(fp);
|
||
}
|
||
out[0] = 0;
|
||
}
|
||
#endif
|
||
|
||
/* Fallback: console prompt (headless / Windows / no zenity). */
|
||
fprintf(stderr, "CST-Agent: enter MiniMax API Key (saved to config dir): ");
|
||
fflush(stderr);
|
||
if (!fgets(out, (int)out_sz, stdin)) {
|
||
out[0] = 0;
|
||
return -1;
|
||
}
|
||
size_t len = strlen(out);
|
||
while (len > 0 && (out[len - 1] == '\n' || out[len - 1] == '\r'))
|
||
out[--len] = 0;
|
||
if (len == 0) {
|
||
out[0] = 0;
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int cst_creds_ensure_api_key(char *out, size_t out_sz) {
|
||
const char *env = getenv("CST_API_KEY");
|
||
if (!env || !env[0])
|
||
env = getenv("MINIMAX_API_KEY");
|
||
if (!env || !env[0])
|
||
env = getenv("OPENAI_API_KEY");
|
||
if (env && env[0]) {
|
||
snprintf(out, out_sz, "%s", env);
|
||
return 0;
|
||
}
|
||
if (cst_creds_load_api_key(out, out_sz) == 0 && out[0])
|
||
return 0;
|
||
if (cst_creds_prompt_api_key(out, out_sz) != 0)
|
||
return -1;
|
||
if (cst_creds_save_api_key(out) != 0)
|
||
fprintf(stderr, "warning: failed to save credentials file\n");
|
||
else
|
||
fprintf(stderr, "saved API key to config directory\n");
|
||
return 0;
|
||
}
|