feat: parliament daemon, login gate, peer context sync (0.9.13)
Login claims the chair, writes a session ticket, and starts a background service for discovery plus revision-based context sync. Password Realm opens require --login (or CST_REALM_PASSWORD); --service/--service-stop manage the resident agent. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+132
-2
@@ -298,7 +298,7 @@ int cst_peer_dial(const char *host, int port, const cst_realm_cert_t *self,
|
||||
out->remote = remote;
|
||||
snprintf(out->host, sizeof(out->host), "%s", host);
|
||||
out->port = port;
|
||||
os_net_close(s);
|
||||
out->sock = (intptr_t)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -329,6 +329,136 @@ int cst_peer_accept(intptr_t listen_sock, const cst_realm_cert_t *self,
|
||||
out->remote = remote;
|
||||
os_net_peer(s, out->host, sizeof(out->host));
|
||||
out->port = 0;
|
||||
os_net_close(s);
|
||||
out->sock = (intptr_t)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cst_peer_close(cst_peer_session_t *s) {
|
||||
if (!s)
|
||||
return;
|
||||
if (s->sock != 0 && s->sock != (intptr_t)OS_SOCKET_BAD)
|
||||
os_net_close((os_socket_t)s->sock);
|
||||
s->sock = (intptr_t)OS_SOCKET_BAD;
|
||||
}
|
||||
|
||||
static int send_sync_hdr(os_socket_t s, const cst_peer_sync_bundle_t *b,
|
||||
int timeout_ms) {
|
||||
cJSON *o = cJSON_CreateObject();
|
||||
if (!o)
|
||||
return -1;
|
||||
cJSON_AddNumberToObject(o, "v", 1);
|
||||
cJSON_AddStringToObject(o, "op", "sync");
|
||||
cJSON_AddNumberToObject(o, "revision", (double)b->revision);
|
||||
cJSON_AddStringToObject(o, "parliament_id", b->parliament_id);
|
||||
cJSON_AddStringToObject(o, "project_id", b->project_id);
|
||||
cJSON_AddStringToObject(o, "chair", b->chair_instance);
|
||||
size_t clen = b->context ? strlen(b->context) : 0;
|
||||
cJSON_AddNumberToObject(o, "context_len", (double)clen);
|
||||
char *line = cJSON_PrintUnformatted(o);
|
||||
cJSON_Delete(o);
|
||||
if (!line)
|
||||
return -1;
|
||||
int rc = send_line(s, line, timeout_ms);
|
||||
free(line);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
if (clen > 0 &&
|
||||
os_net_send_all(s, b->context, clen, timeout_ms) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_sync_hdr(os_socket_t s, cst_peer_sync_bundle_t *b,
|
||||
int timeout_ms) {
|
||||
memset(b, 0, sizeof(*b));
|
||||
char *line = recv_line(s, timeout_ms);
|
||||
if (!line)
|
||||
return -1;
|
||||
cJSON *o = cJSON_Parse(line);
|
||||
free(line);
|
||||
if (!o)
|
||||
return -1;
|
||||
const cJSON *op = cJSON_GetObjectItemCaseSensitive(o, "op");
|
||||
if (!cJSON_IsString(op) || strcmp(op->valuestring, "sync") != 0) {
|
||||
cJSON_Delete(o);
|
||||
return -1;
|
||||
}
|
||||
const cJSON *v;
|
||||
v = cJSON_GetObjectItemCaseSensitive(o, "revision");
|
||||
if (cJSON_IsNumber(v))
|
||||
b->revision = (unsigned long)v->valuedouble;
|
||||
v = cJSON_GetObjectItemCaseSensitive(o, "parliament_id");
|
||||
if (cJSON_IsString(v))
|
||||
snprintf(b->parliament_id, sizeof(b->parliament_id), "%s", v->valuestring);
|
||||
v = cJSON_GetObjectItemCaseSensitive(o, "project_id");
|
||||
if (cJSON_IsString(v))
|
||||
snprintf(b->project_id, sizeof(b->project_id), "%s", v->valuestring);
|
||||
v = cJSON_GetObjectItemCaseSensitive(o, "chair");
|
||||
if (cJSON_IsString(v))
|
||||
snprintf(b->chair_instance, sizeof(b->chair_instance), "%s",
|
||||
v->valuestring);
|
||||
size_t clen = 0;
|
||||
v = cJSON_GetObjectItemCaseSensitive(o, "context_len");
|
||||
if (cJSON_IsNumber(v) && v->valuedouble >= 0)
|
||||
clen = (size_t)v->valuedouble;
|
||||
cJSON_Delete(o);
|
||||
if (clen > 2 * 1024 * 1024)
|
||||
return -1;
|
||||
if (clen == 0) {
|
||||
b->context = strdup("");
|
||||
return b->context ? 0 : -1;
|
||||
}
|
||||
char *ctx = malloc(clen + 1);
|
||||
if (!ctx)
|
||||
return -1;
|
||||
size_t got = 0;
|
||||
while (got < clen) {
|
||||
long r = os_net_recv(s, ctx + got, clen - got, timeout_ms);
|
||||
if (r <= 0) {
|
||||
free(ctx);
|
||||
return -1;
|
||||
}
|
||||
got += (size_t)r;
|
||||
}
|
||||
ctx[clen] = 0;
|
||||
b->context = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cst_peer_sync(cst_peer_session_t *s, cst_peer_sync_bundle_t *local,
|
||||
int timeout_ms, char *err, size_t err_sz) {
|
||||
if (err && err_sz)
|
||||
err[0] = 0;
|
||||
if (!s || !local || s->sock == (intptr_t)OS_SOCKET_BAD) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "bad peer session");
|
||||
return -1;
|
||||
}
|
||||
os_socket_t sock = (os_socket_t)s->sock;
|
||||
if (send_sync_hdr(sock, local, timeout_ms) != 0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "sync send failed");
|
||||
return -1;
|
||||
}
|
||||
cst_peer_sync_bundle_t remote;
|
||||
if (read_sync_hdr(sock, &remote, timeout_ms) != 0) {
|
||||
if (err && err_sz)
|
||||
snprintf(err, err_sz, "sync recv failed");
|
||||
return -1;
|
||||
}
|
||||
if (remote.revision > local->revision) {
|
||||
free(local->context);
|
||||
local->context = remote.context;
|
||||
remote.context = NULL;
|
||||
local->revision = remote.revision;
|
||||
snprintf(local->parliament_id, sizeof(local->parliament_id), "%s",
|
||||
remote.parliament_id);
|
||||
snprintf(local->project_id, sizeof(local->project_id), "%s",
|
||||
remote.project_id);
|
||||
snprintf(local->chair_instance, sizeof(local->chair_instance), "%s",
|
||||
remote.chair_instance);
|
||||
} else {
|
||||
free(remote.context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user