OpenCode-style TUI, model probing, sandboxed auto approval
- TUI: centered home screen with logo, boxed input with mode line, sidebar (>=120 cols), Ctrl+P palette, Tab Build/Plan, Shift+Tab approval cycle, mouse (cards, cursor, pickers, wheel), /sidebar /mouse /approval - /models probing: key check on entry (401 re-asks), API models in /model - Shell sandbox in C: Linux Landlock (writes limited to workspace/tmp, ABI>=4 blocks TCP), macOS sandbox_init, rules fallback elsewhere - Approval modes ask/auto/yolo (default auto); read-only command whitelist; ask to rerun outside the sandbox when a command looks blocked - Upgradable bootstrap scripts (git pull --ff-only, rebuild, reinstall) and cst-agent-update; cst-agent --version - tests: test_sandbox, test_models
This commit is contained in:
+37
-4
@@ -1,5 +1,6 @@
|
||||
#include "cst_agent.h"
|
||||
#include "cst_tools.h"
|
||||
#include "cst_version.h"
|
||||
#include "os_compat.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
@@ -26,7 +27,9 @@ static void usage(const char *argv0) {
|
||||
" -N, --new start a new session\n"
|
||||
" -R, --resume ID open a session by id or unique prefix\n"
|
||||
" -L, --list-sessions list sessions and exit\n"
|
||||
" -y, --yolo never ask before shell / writes / web\n"
|
||||
" -A, --approval MODE ask | auto (default: edits run, shell in\n"
|
||||
" the sandbox or by safe rules) | yolo\n"
|
||||
" -y, --yolo same as --approval yolo (this run only)\n"
|
||||
" -C, --confirm LIST shell,write,net (default) | none\n"
|
||||
" -r, --protocol P native-eos (default) | native-inc | ctl |\n"
|
||||
" ctl-heredoc | xml\n"
|
||||
@@ -38,6 +41,7 @@ static void usage(const char *argv0) {
|
||||
" -b, --api-base URL OpenAI-compatible base URL (…/v1)\n"
|
||||
" -s, --session FILE explicit session file\n"
|
||||
" -d, --demo-ctl local CTL parser demo (no API)\n"
|
||||
" -V, --version print the version and exit\n"
|
||||
"\n"
|
||||
"API keys: the TUI asks for a missing key and saves it per provider\n"
|
||||
" in the config dir (credentials, mode 0600); /key changes it.\n"
|
||||
@@ -48,7 +52,8 @@ static void usage(const char *argv0) {
|
||||
" CST_CONFIRM CST_MAX_STEPS CST_TOOL_OUT_CAP CST_CTX_WINDOW\n"
|
||||
" CST_CTX_TOKENS CST_AUTO_COMPACT=0 CST_SHELL_TIMEOUT\n"
|
||||
" CST_METRICS=0 CST_RECORD_SSE CST_REPLAY_SSE CST_MODELS\n"
|
||||
" CST_AUTO_APPROVE=1 CST_SHELL CST_NO_PROJECT_NOTES=1 NO_COLOR\n"
|
||||
" CST_APPROVAL=ask|auto|yolo CST_AUTO_APPROVE=1 (=yolo)\n"
|
||||
" CST_SANDBOX=off CST_SHELL CST_NO_PROJECT_NOTES=1 NO_COLOR\n"
|
||||
"TUI: type /help inside for commands and keys\n",
|
||||
argv0);
|
||||
}
|
||||
@@ -235,6 +240,15 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int approval = -1;
|
||||
const char *env_appr = getenv("CST_APPROVAL");
|
||||
if (env_appr && env_appr[0] && cst_approval_parse(env_appr, &approval) != 0) {
|
||||
fprintf(stderr, "bad CST_APPROVAL=%s (ask | auto | yolo)\n", env_appr);
|
||||
return 1;
|
||||
}
|
||||
if (cfg.auto_approve)
|
||||
approval = CST_APPROVAL_YOLO;
|
||||
|
||||
int do_demo = 0;
|
||||
int force_ask_key = 0;
|
||||
int session_explicit = 0;
|
||||
@@ -261,12 +275,14 @@ int main(int argc, char **argv) {
|
||||
{"resume", required_argument, 0, 'R'},
|
||||
{"list-sessions", no_argument, 0, 'L'},
|
||||
{"yolo", no_argument, 0, 'y'},
|
||||
{"approval", required_argument, 0, 'A'},
|
||||
{"confirm", required_argument, 0, 'C'},
|
||||
{"effort", required_argument, 0, 'e'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{0, 0, 0, 0}};
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "w:s:b:k:m:P:r:n:T:Hp:dahNcR:LyC:e:", opts, NULL)) !=
|
||||
while ((c = getopt_long(argc, argv, "w:s:b:k:m:P:r:n:T:Hp:dahVNcR:LyA:C:e:", opts, NULL)) !=
|
||||
-1) {
|
||||
switch (c) {
|
||||
case 'w':
|
||||
@@ -334,7 +350,14 @@ int main(int argc, char **argv) {
|
||||
do_list = 1;
|
||||
break;
|
||||
case 'y':
|
||||
cfg.auto_approve = 1;
|
||||
approval = CST_APPROVAL_YOLO;
|
||||
break;
|
||||
case 'A':
|
||||
if (cst_approval_parse(optarg, &approval) != 0) {
|
||||
fprintf(stderr, "bad --approval %s (ask | auto | yolo)\n", optarg);
|
||||
curl_global_cleanup();
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
if (parse_confirm(optarg, &cfg) != 0) {
|
||||
@@ -356,6 +379,10 @@ int main(int argc, char **argv) {
|
||||
usage(argv[0]);
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
case 'V':
|
||||
printf("cst-agent %s\n", CST_VERSION);
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
curl_global_cleanup();
|
||||
@@ -425,6 +452,12 @@ int main(int argc, char **argv) {
|
||||
while (alen > 0 && cfg.api_base[alen - 1] == '/')
|
||||
cfg.api_base[--alen] = 0;
|
||||
|
||||
/* Option > environment > last picked (settings.json) > auto. */
|
||||
if (approval < 0 && cst_approval_parse(saved.approval, &approval) != 0)
|
||||
approval = CST_DEFAULT_APPROVAL;
|
||||
cfg.approval = approval;
|
||||
cfg.auto_approve = approval == CST_APPROVAL_YOLO;
|
||||
|
||||
/* Headless defaults to auto-approve shell/write (no interactive confirm). */
|
||||
if (cfg.headless)
|
||||
cfg.auto_approve = 1;
|
||||
|
||||
Reference in New Issue
Block a user