forked from WHC/Hermit
v0.8.0: WebUI redesign, /web, workspace switch, 75% budget, no shell timeout
The front end is rebuilt on the design tokens the desktop app ships (surfaces, color-mix borders, alpha hovers, accent): a 30-icon inline SVG sprite, dark and light themes, layout switches as deep links (?side=0, ?panel=0, ?theme=light) resolved before the first paint. The empty page is one line and leaves with the first turn; the right rail keeps three cards - todo, file changes (+N -M) and context (tokens, prompt cache hit rate). Both rails animate in and out, and a collapsed rail widens its grid track to 0 instead of being removed, so the transcript never slides into a zero-width column. - src/webui.c: POST /api/workspace - re-root the agent at another directory (permission rules, metrics, AGENTS.md follow) and start a session there, as sessions live per directory; state also reports cached/cache_miss tokens - src/agent_loop.c: cst_agent_set_workspace(), rebuilt prompt and re-measured budget; the auto-compact threshold is 75% of the window (was 60%) - src/tui.c: /web forks a WebUI next to the TUI - separate process and a new session, because cst_webui_run() blocks and two writers corrupt one session - src/os_compat.c, src/os_win.c: run_shell runs without a time limit unless CST_SHELL_TIMEOUT asks for one, so builds and long clones finish - src/main.c, include/cst_webui.h: one CST_WEBUI_DEFAULT_PORT for --help, the docs and /web; Termux opens the browser through termux-open-url - README.md, docs/webui.md, docs/env_openai.md: /web, /api/workspace, the new state fields, the 75% threshold and the layout deep links tests/e2e_webui.sh green (permission round-trip, token paths, --web-dev); make check green.
This commit is contained in:
+39
-1
@@ -181,6 +181,8 @@ static cJSON *wui_state_new(wui_t *w) {
|
||||
cJSON_AddNumberToObject(u, "requests", sess.requests);
|
||||
cJSON_AddNumberToObject(u, "prompt_tokens", (double)sess.prompt);
|
||||
cJSON_AddNumberToObject(u, "completion_tokens", (double)sess.out);
|
||||
cJSON_AddNumberToObject(u, "cached_tokens", (double)sess.cached);
|
||||
cJSON_AddNumberToObject(u, "cache_miss_tokens", (double)sess.miss);
|
||||
cJSON_AddNumberToObject(u, "cost", sess.cost);
|
||||
cJSON_AddBoolToObject(u, "priced", sess.priced != 0);
|
||||
cJSON_AddStringToObject(u, "currency", sess.currency);
|
||||
@@ -916,6 +918,38 @@ static int h_session_new(cst_http_conn_t *c, void *ud) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* POST /api/workspace {"path":"/abs/dir"} - re-root the agent. The session
|
||||
* store is per directory, so this always lands in a fresh session there. */
|
||||
static int h_workspace(cst_http_conn_t *c, void *ud) {
|
||||
wui_t *w = ud;
|
||||
const cst_http_req_t *req = cst_http_request(c);
|
||||
cJSON *j = req->body_len ? cJSON_Parse(req->body) : NULL;
|
||||
const cJSON *p = j ? cJSON_GetObjectItem(j, "path") : NULL;
|
||||
if (!cJSON_IsString(p) || !p->valuestring[0]) {
|
||||
cJSON_Delete(j);
|
||||
return cst_http_send_err(c, 400, "path is required");
|
||||
}
|
||||
char err[256] = "";
|
||||
int rc = cst_agent_set_workspace(w->agent, p->valuestring, err, sizeof(err));
|
||||
cJSON_Delete(j);
|
||||
if (rc != 0)
|
||||
return cst_http_send_err(c, 400, err[0] ? err : "cannot switch workspace");
|
||||
char id[128] = "";
|
||||
if (cst_agent_new_session(w->agent, id, sizeof(id)) != 0)
|
||||
return cst_http_send_err(c, 500, "cannot start a session there");
|
||||
wui_emit_str(w, "session", "id", id);
|
||||
wui_emit_state(w);
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
if (!o)
|
||||
return cst_http_send_err(c, 500, "out of memory");
|
||||
cJSON_AddStringToObject(o, "id", id);
|
||||
char *json = cJSON_PrintUnformatted(o);
|
||||
cJSON_Delete(o);
|
||||
rc = cst_http_send_json(c, 200, json);
|
||||
free(json);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int h_session_switch(cst_http_conn_t *c, void *ud) {
|
||||
wui_t *w = ud;
|
||||
const cst_http_req_t *req = cst_http_request(c);
|
||||
@@ -1134,7 +1168,10 @@ static void open_browser(const char *url) {
|
||||
os_run_shell_c(".", cmd, 0, NULL);
|
||||
#else
|
||||
char cmd[600];
|
||||
snprintf(cmd, sizeof(cmd), "xdg-open '%s' >/dev/null 2>&1 &", url);
|
||||
/* Termux has no xdg-open; termux-open-url hands the URL to Android. */
|
||||
const char *opener =
|
||||
getenv("TERMUX_VERSION") ? "termux-open-url" : "xdg-open";
|
||||
snprintf(cmd, sizeof(cmd), "%s '%s' >/dev/null 2>&1 &", opener, url);
|
||||
os_run_shell_c(".", cmd, 0, NULL);
|
||||
#endif
|
||||
(void)url;
|
||||
@@ -1173,6 +1210,7 @@ int cst_webui_run(cst_agent_t *agent, const cst_webui_opts_t *opts) {
|
||||
{"GET", "/api/sessions", h_sessions, NULL, 0, 0},
|
||||
{"POST", "/api/sessions/new", h_session_new, NULL, 0, 0},
|
||||
{"POST", "/api/sessions/switch", h_session_switch, NULL, 0, 0},
|
||||
{"POST", "/api/workspace", h_workspace, NULL, 0, 0},
|
||||
{"POST", "/api/turn", h_turn, NULL, 0, 0},
|
||||
{"POST", "/api/cancel", h_cancel, NULL, 0, 0},
|
||||
{"POST", "/api/permission", h_permission, NULL, 0, 0},
|
||||
|
||||
Reference in New Issue
Block a user