code wiki / _hdl_build / nx_connect_api_lib.nx

nx_connect_api_lib.nx source

↩ module page · 368 lines · 20182 B

1// nx_connect_api_lib.nx -- the CONNECT FRONT DOOR: one stateless verb dispatcher over all 8 capability 2// engines, emitting versioned JSON. This is what makes CONNECT MCP-ready, API-ready, agent-ready, 3// workflow-ready, and horizontally scalable in ONE surface: 4// 5// * OBJECT-ORIENTED: the 8 `nx_connect_*_lib.nx` organs ARE the objects -- each encapsulates one 6// capability's state contract and operations (single responsibility). THIS file is the facade/registry 7// over them; it owns no capability logic, only routing + serialization. 8// * MCP + API: a thin CLI (nx_connect_api.nx) wraps ca_dispatch and is registered via 9// POST /api/tools/register, so it appears as mcp__nishi__nx_connect and is drivable over the mgmt API. 10// * AGENTIC + WORKFLOW: the `help` verb returns a machine-readable catalog of every verb and its argument 11// shape, so an agent or a Workflow can DISCOVER the interface at runtime instead of being told it. 12// * SCALE: every verb is a PURE function of its arguments -- no shared mutable state, no session, no disk. 13// Any number of calls run independently and in parallel; the tool can be replicated with zero coordination. 14// * CONTRACT STABILITY (rule 19): every response carries {"v":1,...}; new fields are additive. 15// * STRUCTURED ERRORS (rule 6): a bad call returns {"ok":0,"error":<slug>,"detail":<human>}, never a crash. 16// 17// PURE CORE: no main (so the gate imports it and drives ca_dispatch with synthetic argv). license_tier: ORIGINAL 18import "nx_connect_match_lib.nx" 19import "nx_connect_lds_reconnect_lib.nx" 20import "nx_connect_youth_lib.nx" 21import "nx_connect_wards_lib.nx" 22import "nx_connect_lanes_lib.nx" 23import "nx_connect_events_lib.nx" 24import "nx_connect_polls_lib.nx" 25import "nx_connect_group_lib.nx" 26import "nx_connect_graph_lib.nx" 27import "nx_mt_multi.nx" 28const CA_MAGIC_4096: i64 = 4096 29const CA_MAGIC_4095: i64 = 4095 30 31const CA_VER: i64 = 1 32const CA_LANG_MAX: i64 = 4 // MM_EN=0 MM_RU=1 MM_BE=2 MM_UK=3 MM_PL=4 33 34// ---- serialization + arg helpers (self-contained; JSON, not HTML) ---- 35func ca_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 36func ca_puts(o: *u8, off: i64, s: *u8) -> i64 { var p: i64=off; var i: i64=0; while s[i]!=(0 as u8){o[p]=s[i]; p=p+1; i=i+1} return p } 37// base-10 int, allocation-free, handles negatives (excluded sentinels are -1) 38func ca_putn(o: *u8, off: i64, v: i64) -> i64 { 39 var p: i64=off 40 var x: i64=v 41 if x<0 { o[p]=45 as u8; p=p+1; x=0-x } 42 if x==0 { o[p]=48 as u8; return p+1 } 43 var d: i64=1 44 var m: i64=x 45 while m>=10 { d=d*10; m=m/10 } 46 while d>0 { let q: i64=x/d; o[p]=(48+q) as u8; p=p+1; x=x-(q*d); d=d/10 } 47 return p 48} 49func ca_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8){ if a[i]!=b[i]{return 0} i=i+1 } if b[i]!=(0 as u8){return 0} return 1 } 50// parse a (possibly negative) base-10 int from a NUL-terminated arg 51func ca_atoi(s: *u8) -> i64 { 52 var i: i64=0 53 var neg: i64=0 54 if s[0]==(45 as u8) { neg=1; i=1 } 55 var v: i64=0 56 while s[i]!=(0 as u8) { let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } 57 if neg==1 { return 0-v } 58 return v 59} 60// i-th positional arg as an int (0 if absent) -- argv[0] is the verb, args start at 1 61func ca_ia(argv: *i64, argc: i64, i: i64) -> i64 { if i>=argc { return 0 } return ca_atoi(argv[i] as *u8) } 62 63// JSON envelope: {"v":1,"tool":"nx_connect","verb":"<v>", (caller closes) 64func ca_open(o: *u8, off: i64, verb: *u8) -> i64 { 65 var p: i64 = ca_puts(o, off, "{\"v\":" as *u8) 66 p = ca_putn(o, p, CA_VER) 67 p = ca_puts(o, p, ",\"tool\":\"nx_connect\",\"verb\":\"" as *u8) 68 p = ca_puts(o, p, verb) 69 p = ca_puts(o, p, "\"," as *u8) 70 return p 71} 72// structured error close 73func ca_err(o: *u8, off: i64, verb: *u8, slug: *u8, detail: *u8) -> i64 { 74 var p: i64 = ca_open(o, off, verb) 75 p = ca_puts(o, p, "\"ok\":0,\"error\":\"" as *u8) 76 p = ca_puts(o, p, slug) 77 p = ca_puts(o, p, "\",\"detail\":\"" as *u8) 78 p = ca_puts(o, p, detail) 79 p = ca_puts(o, p, "\"}" as *u8) 80 return p 81} 82// escape a string for a JSON value: backslash and double-quote get escaped, control bytes (<0x20) become 83// spaces, everything else -- including raw UTF-8 continuation bytes (Cyrillic etc.) -- passes through, which 84// is valid JSON. This is the JSON analogue of the app's HTML escaper; the front door must never emit a 85// string that breaks its own contract. 86func ca_esc_json(o: *u8, off: i64, s: *u8) -> i64 { 87 var p: i64=off 88 var i: i64=0 89 while s[i]!=(0 as u8) { 90 let c: i64 = s[i] as i64 91 if c==92 { o[p]=92 as u8; o[p+1]=92 as u8; p=p+2 } else { 92 if c==34 { o[p]=92 as u8; o[p+1]=34 as u8; p=p+2 } else { 93 if c>=0 { if c<32 { o[p]=32 as u8; p=p+1 } else { o[p]=s[i]; p=p+1 } } else { o[p]=s[i]; p=p+1 } } } 94 i=i+1 95 } 96 return p 97} 98 99// one {"key":int} pair 100func ca_kv(o: *u8, off: i64, key: *u8, v: i64) -> i64 { 101 var p: i64 = ca_puts(o, off, "\"" as *u8) 102 p = ca_puts(o, p, key) 103 p = ca_puts(o, p, "\":" as *u8) 104 p = ca_putn(o, p, v) 105 return p 106} 107 108// ---- the self-describing catalog (agentic/workflow discovery) ---- 109func ca_help(o: *u8, off: i64) -> i64 { 110 var p: i64 = ca_open(o, off, "help" as *u8) 111 p = ca_puts(o, p, "\"ok\":1,\"stateless\":1,\"verbs\":[" as *u8) 112 p = ca_puts(o, p, "{\"name\":\"match\",\"args\":\"a_age a_learn a_teach a_int b_age b_learn b_teach b_int lane\",\"returns\":\"weight 0..100 or excluded=-1\"}," as *u8) 113 p = ca_puts(o, p, "{\"name\":\"reconnect\",\"args\":\"a_band a_mission a_area a_start a_end a_optin b_band b_mission b_area b_start b_end b_optin lane\",\"returns\":\"score 0..100 or excluded=-1\"}," as *u8) 114 p = ca_puts(o, p, "{\"name\":\"lane\",\"args\":\"a_req b_req a_adult b_adult blocked\",\"returns\":\"active lane 0..3 or -1, plus name\"}," as *u8) 115 p = ca_puts(o, p, "{\"name\":\"youth\",\"args\":\"a_band b_band guardian ctx adults\",\"returns\":\"allowed 0|1\"}," as *u8) 116 p = ca_puts(o, p, "{\"name\":\"wards\",\"args\":\"n class0 class1 ..\",\"returns\":\"forbidden field count (0=community-safe)\"}," as *u8) 117 p = ca_puts(o, p, "{\"name\":\"events_rsvp\",\"args\":\"cap is_youth adults_present actor_minor\",\"returns\":\"rsvp code 1|-2|-3|-4\"}," as *u8) 118 p = ca_puts(o, p, "{\"name\":\"poll\",\"args\":\"c0 c1 c2 newchoice\",\"returns\":\"tally after a voter on opt0 re-votes (replace)\"}," as *u8) 119 p = ca_puts(o, p, "{\"name\":\"group_remove\",\"args\":\"actor_role target_role target_is_owner\",\"returns\":\"removed 0|1\"}," as *u8) 120 p = ca_puts(o, p, "{\"name\":\"translate\",\"args\":\"src_lang dst_lang text\",\"returns\":\"inline translation, original preserved (unlimited, no quota)\"}," as *u8) 121 p = ca_puts(o, p, "{\"name\":\"graph\",\"args\":\"a_band a_teach a_learn a_hob a_faith a_optin a_place b_band b_teach b_learn b_hob b_faith b_optin b_place lane\",\"returns\":\"unified interest-graph score 0..100 or excluded=-1 (one model over language+hobby+faith+place)\"}" as *u8) 122 p = ca_puts(o, p, "],\"lanes\":\"0=language 1=friend 2=community 3=date\",\"bands\":\"0=minor 1=adult\",\"langs\":\"0=en 1=ru 2=be 3=uk 4=pl\"}" as *u8) 123 return p 124} 125 126// graph <a: band teach learn hob faith optin place> <b: same 7> <lane> -- the UNIFIED interest graph, one 127// model over language+hobby+faith+place, so two people with no shared language still connect on a shared 128// hobby/faith in the friendship lane. Faith is consent-gated; the minor<->romance wall holds. 129func ca_v_graph(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 130 if argc < 16 { return ca_err(o, off, "graph" as *u8, "usage" as *u8, "graph needs 2 nodes of 7 fields + lane = 15 int args" as *u8) } 131 let a: *i64 = sys_mmap(GX_NODE_SLOTS*8) as *i64 132 let b: *i64 = sys_mmap(GX_NODE_SLOTS*8) as *i64 133 gx_mk(a, ca_ia(argv,argc,1),ca_ia(argv,argc,2),ca_ia(argv,argc,3),ca_ia(argv,argc,4),ca_ia(argv,argc,5),ca_ia(argv,argc,6),ca_ia(argv,argc,7)) 134 gx_mk(b, ca_ia(argv,argc,8),ca_ia(argv,argc,9),ca_ia(argv,argc,10),ca_ia(argv,argc,11),ca_ia(argv,argc,12),ca_ia(argv,argc,13),ca_ia(argv,argc,14)) 135 let s: i64 = gx_graph_score(a, b, ca_ia(argv,argc,15)) 136 var p: i64 = ca_open(o, off, "graph" as *u8) 137 p = ca_puts(o, p, "\"ok\":1," as *u8) 138 p = ca_kv(o, p, "score" as *u8, s) 139 if s<0 { p = ca_puts(o, p, ",\"excluded\":1" as *u8) } 140 p = ca_puts(o, p, "}" as *u8) 141 return p 142} 143 144// translate <src_lang> <dst_lang> <text> -- the language-exchange CORE, exposed for agents. The original is 145// echoed back beside the translation (the learning loop), and it is unlimited BY CONSTRUCTION: there is no 146// quota state anywhere in this call. Unknown words pass through untranslated -- nothing is ever dropped. 147func ca_v_translate(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 148 if argc < 4 { return ca_err(o, off, "translate" as *u8, "usage" as *u8, "translate needs src_lang dst_lang text" as *u8) } 149 let sl: i64 = ca_ia(argv, argc, 1) 150 let dl: i64 = ca_ia(argv, argc, 2) 151 if sl<0 { return ca_err(o, off, "translate" as *u8, "range" as *u8, "src_lang out of 0..4" as *u8) } 152 if sl>CA_LANG_MAX { return ca_err(o, off, "translate" as *u8, "range" as *u8, "src_lang out of 0..4" as *u8) } 153 if dl<0 { return ca_err(o, off, "translate" as *u8, "range" as *u8, "dst_lang out of 0..4" as *u8) } 154 if dl>CA_LANG_MAX { return ca_err(o, off, "translate" as *u8, "range" as *u8, "dst_lang out of 0..4" as *u8) } 155 let src: *u8 = argv[3] as *u8 156 let tr: *u8 = sys_mmap(CA_MAGIC_4096) 157 let tn: i64 = nx_multi_translate(src, ca_slen(src), sl, dl, tr, CA_MAGIC_4095) 158 tr[tn]=0 as u8 159 var p: i64 = ca_open(o, off, "translate" as *u8) 160 p = ca_puts(o, p, "\"ok\":1,\"original\":\"" as *u8) 161 p = ca_esc_json(o, p, src) 162 p = ca_puts(o, p, "\",\"translation\":\"" as *u8) 163 p = ca_esc_json(o, p, tr) 164 p = ca_puts(o, p, "\",\"unlimited\":1}" as *u8) 165 return p 166} 167 168// ---- verb handlers (each PURE: builds JSON from args, calls the engine, never mutates shared state) ---- 169 170func ca_v_match(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 171 if argc < 10 { return ca_err(o, off, "match" as *u8, "usage" as *u8, "match needs 9 int args" as *u8) } 172 let w: i64 = mx_match_weight(ca_ia(argv,argc,1),ca_ia(argv,argc,2),ca_ia(argv,argc,3),ca_ia(argv,argc,4), 173 ca_ia(argv,argc,5),ca_ia(argv,argc,6),ca_ia(argv,argc,7),ca_ia(argv,argc,8),ca_ia(argv,argc,9)) 174 var p: i64 = ca_open(o, off, "match" as *u8) 175 p = ca_puts(o, p, "\"ok\":1," as *u8) 176 p = ca_kv(o, p, "weight" as *u8, w) 177 if w<0 { p = ca_puts(o, p, ",\"excluded\":1" as *u8) } 178 p = ca_puts(o, p, "}" as *u8) 179 return p 180} 181 182func ca_v_reconnect(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 183 if argc < 14 { return ca_err(o, off, "reconnect" as *u8, "usage" as *u8, "reconnect needs 13 int args" as *u8) } 184 let a: *i64 = sys_mmap(RC_REC_SLOTS*8) as *i64 185 let b: *i64 = sys_mmap(RC_REC_SLOTS*8) as *i64 186 // tokens/confirm fixed at 0 in v1 (the confirmed-edge bonus is not exposed): a=1/0, b=2/0 187 rc_mk(a, ca_ia(argv,argc,1), ca_ia(argv,argc,2), ca_ia(argv,argc,3), ca_ia(argv,argc,4), ca_ia(argv,argc,5), ca_ia(argv,argc,6), 1, 0) 188 rc_mk(b, ca_ia(argv,argc,7), ca_ia(argv,argc,8), ca_ia(argv,argc,9), ca_ia(argv,argc,10), ca_ia(argv,argc,11), ca_ia(argv,argc,12), 2, 0) 189 let s: i64 = rc_lane(a, b, ca_ia(argv,argc,13)) 190 var p: i64 = ca_open(o, off, "reconnect" as *u8) 191 p = ca_puts(o, p, "\"ok\":1," as *u8) 192 p = ca_kv(o, p, "score" as *u8, s) 193 if s<0 { p = ca_puts(o, p, ",\"excluded\":1" as *u8) } 194 p = ca_puts(o, p, "}" as *u8) 195 return p 196} 197 198func ca_v_lane(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 199 if argc < 6 { return ca_err(o, off, "lane" as *u8, "usage" as *u8, "lane needs 5 int args" as *u8) } 200 let l: i64 = ln_active_lane(ca_ia(argv,argc,1),ca_ia(argv,argc,2),ca_ia(argv,argc,3),ca_ia(argv,argc,4),ca_ia(argv,argc,5)) 201 var p: i64 = ca_open(o, off, "lane" as *u8) 202 p = ca_puts(o, p, "\"ok\":1," as *u8) 203 p = ca_kv(o, p, "lane" as *u8, l) 204 p = ca_puts(o, p, ",\"name\":\"" as *u8) 205 p = ca_puts(o, p, ln_lane_name(l)) 206 p = ca_puts(o, p, "\"}" as *u8) 207 return p 208} 209 210func ca_v_youth(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 211 if argc < 6 { return ca_err(o, off, "youth" as *u8, "usage" as *u8, "youth needs 5 int args" as *u8) } 212 let a: i64 = yw_may_channel(ca_ia(argv,argc,1),ca_ia(argv,argc,2),ca_ia(argv,argc,3),ca_ia(argv,argc,4),ca_ia(argv,argc,5)) 213 var p: i64 = ca_open(o, off, "youth" as *u8) 214 p = ca_puts(o, p, "\"ok\":1," as *u8) 215 p = ca_kv(o, p, "allowed" as *u8, a) 216 p = ca_puts(o, p, "}" as *u8) 217 return p 218} 219 220func ca_v_wards(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 221 if argc < 2 { return ca_err(o, off, "wards" as *u8, "usage" as *u8, "wards needs n then n class ints" as *u8) } 222 let n: i64 = ca_ia(argv,argc,1) 223 if n < 0 { return ca_err(o, off, "wards" as *u8, "range" as *u8, "n must be >= 0" as *u8) } 224 if n > FR_MAX { return ca_err(o, off, "wards" as *u8, "range" as *u8, "n exceeds FR_MAX" as *u8) } 225 if argc < 2+n { return ca_err(o, off, "wards" as *u8, "usage" as *u8, "fewer class args than n" as *u8) } 226 let classes: *i64 = sys_mmap(FR_MAX*8) as *i64 227 var i: i64=0 228 while i<n { classes[i]=ca_ia(argv,argc,2+i); i=i+1 } 229 let f: i64 = wd_forbidden_count(classes, n) 230 var p: i64 = ca_open(o, off, "wards" as *u8) 231 p = ca_puts(o, p, "\"ok\":1," as *u8) 232 p = ca_kv(o, p, "forbidden" as *u8, f) 233 p = ca_puts(o, p, ",\"community_safe\":" as *u8) 234 if f==0 { p = ca_puts(o, p, "1" as *u8) } else { p = ca_puts(o, p, "0" as *u8) } 235 p = ca_puts(o, p, "}" as *u8) 236 return p 237} 238 239// events_rsvp cap is_youth adults_present actor_minor -> the REAL ev_rsvp verdict against a minimal world 240func ca_v_events(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 241 if argc < 5 { return ca_err(o, off, "events_rsvp" as *u8, "usage" as *u8, "events_rsvp needs cap is_youth adults_present actor_minor" as *u8) } 242 let cap: i64 = ca_ia(argv,argc,1) 243 let isyth: i64 = ca_ia(argv,argc,2) 244 let adults: i64 = ca_ia(argv,argc,3) 245 let amin: i64 = ca_ia(argv,argc,4) 246 let e: *i64 = sys_mmap(16*8) as *i64 247 let rsev: *i64 = sys_mmap(RS_MAX*8) as *i64 248 let rsus: *i64 = sys_mmap(RS_MAX*8) as *i64 249 let rsac: *i64 = sys_mmap(RS_MAX*8) as *i64 250 let nrs: *i64 = sys_mmap(8) as *i64 251 let evgrp: *i64 = sys_mmap(EV_MAX*8) as *i64 252 let evcap: *i64 = sys_mmap(EV_MAX*8) as *i64 253 let evyth: *i64 = sys_mmap(EV_MAX*8) as *i64 254 let mbgrp: *i64 = sys_mmap(MB_MAX*8) as *i64 255 let mbus: *i64 = sys_mmap(MB_MAX*8) as *i64 256 let nmb: *i64 = sys_mmap(8) as *i64 257 let uaus: *i64 = sys_mmap(UA_MAX*8) as *i64 258 let uamin: *i64 = sys_mmap(UA_MAX*8) as *i64 259 let nua: *i64 = sys_mmap(8) as *i64 260 e[CX_RSEV]=rsev as i64; e[CX_RSUS]=rsus as i64; e[CX_RSAC]=rsac as i64; e[CX_NRS]=nrs as i64 261 e[CX_EVGRP]=evgrp as i64; e[CX_EVCAP]=evcap as i64; e[CX_EVYTH]=evyth as i64 262 e[CX_MBGRP]=mbgrp as i64; e[CX_MBUS]=mbus as i64; e[CX_NMB]=nmb as i64 263 e[CX_UAUS]=uaus as i64; e[CX_UAMIN]=uamin as i64; e[CX_NUA]=nua as i64 264 evgrp[0]=1; evcap[0]=cap; evyth[0]=isyth 265 // members 1..40 in group 1; adults 1..20 (band 0), youth 21..40 (band 1); actor id 50 266 var i: i64=0 267 var m: i64=0 268 var u: i64=0 269 while i<20 { mbgrp[m]=1; mbus[m]=1+i; m=m+1; uaus[u]=1+i; uamin[u]=0; u=u+1; i=i+1 } 270 i=0 271 while i<20 { mbgrp[m]=1; mbus[m]=21+i; m=m+1; uaus[u]=21+i; uamin[u]=1; u=u+1; i=i+1 } 272 // actor 273 mbgrp[m]=1; mbus[m]=50; m=m+1 274 uaus[u]=50; uamin[u]=amin; u=u+1 275 nmb[0]=m; nua[0]=u 276 // seed `adults` adult RSVPs already present 277 var k: i64=0 278 i=0 279 while i<adults { rsev[k]=0; rsus[k]=1+i; rsac[k]=1; k=k+1; i=i+1 } 280 nrs[0]=k 281 let rc: i64 = ev_rsvp(e, 0, 50) 282 var p: i64 = ca_open(o, off, "events_rsvp" as *u8) 283 p = ca_puts(o, p, "\"ok\":1," as *u8) 284 p = ca_kv(o, p, "code" as *u8, rc) 285 p = ca_puts(o, p, ",\"admitted\":" as *u8) 286 if rc==1 { p = ca_puts(o, p, "1" as *u8) } else { p = ca_puts(o, p, "0" as *u8) } 287 p = ca_puts(o, p, "}" as *u8) 288 return p 289} 290 291// poll c0 c1 c2 newchoice -> seed counts, a voter currently on opt0 re-votes newchoice, return tally 292func ca_v_poll(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 293 if argc < 5 { return ca_err(o, off, "poll" as *u8, "usage" as *u8, "poll needs c0 c1 c2 newchoice" as *u8) } 294 let c0: i64 = ca_ia(argv,argc,1) 295 let c1: i64 = ca_ia(argv,argc,2) 296 let c2: i64 = ca_ia(argv,argc,3) 297 let nc: i64 = ca_ia(argv,argc,4) 298 if c0+c1+c2 >= PL_MAXV { return ca_err(o, off, "poll" as *u8, "range" as *u8, "too many ballots" as *u8) } 299 let bv: *i64 = sys_mmap(PL_MAXV*8) as *i64 300 let bc: *i64 = sys_mmap(PL_MAXV*8) as *i64 301 let nb: *i64 = sys_mmap(8) as *i64 302 let meta: *i64 = sys_mmap(8*8) as *i64 303 nb[0]=0; meta[0]=0; meta[1]=3; meta[2]=1; meta[3]=999 304 var id: i64=1 305 var i: i64=0 306 while i<c0 { pl_vote(bv,bc,nb,meta,id,0); id=id+1; i=i+1 } 307 i=0 308 while i<c1 { pl_vote(bv,bc,nb,meta,id,1); id=id+1; i=i+1 } 309 i=0 310 while i<c2 { pl_vote(bv,bc,nb,meta,id,2); id=id+1; i=i+1 } 311 // voter id 1 is on opt0 (if c0>0); re-vote them to nc -> REPLACE, not append 312 if c0 > 0 { pl_vote(bv,bc,nb,meta,1,nc) } 313 var p: i64 = ca_open(o, off, "poll" as *u8) 314 p = ca_puts(o, p, "\"ok\":1,\"tally\":[" as *u8) 315 p = ca_putn(o, p, pl_count(bc,nb,0)) 316 p = ca_puts(o, p, "," as *u8) 317 p = ca_putn(o, p, pl_count(bc,nb,1)) 318 p = ca_puts(o, p, "," as *u8) 319 p = ca_putn(o, p, pl_count(bc,nb,2)) 320 p = ca_puts(o, p, "]," as *u8) 321 p = ca_kv(o, p, "total" as *u8, nb[0]) 322 p = ca_puts(o, p, "}" as *u8) 323 return p 324} 325 326// group_remove actor_role target_role target_is_owner -> real grp_remove over a minimal ladder 327func ca_v_group(o: *u8, off: i64, argv: *i64, argc: i64) -> i64 { 328 if argc < 4 { return ca_err(o, off, "group_remove" as *u8, "usage" as *u8, "group_remove needs actor_role target_role target_is_owner" as *u8) } 329 let ar: i64 = ca_ia(argv,argc,1) 330 let tr: i64 = ca_ia(argv,argc,2) 331 let towner: i64 = ca_ia(argv,argc,3) 332 let meta: *i64 = sys_mmap(8*8) as *i64 333 let mem: *i64 = sys_mmap(64*8) as *i64 334 grp_create(meta, mem, 1, MODE_GROUP, LANE_LANG) // owner = uid 1 335 grp_add(meta, mem, 1, 8, 0) // actor uid 8 336 grp_promote(meta, mem, 1, 8, ar) 337 var target: i64 = 1 // default target = owner 338 if towner==0 { 339 grp_add(meta, mem, 1, 9, 0) // target uid 9 340 grp_promote(meta, mem, 1, 9, tr) 341 target = 9 342 } 343 let rc: i64 = grp_remove(meta, mem, 8, target) 344 var p: i64 = ca_open(o, off, "group_remove" as *u8) 345 p = ca_puts(o, p, "\"ok\":1," as *u8) 346 p = ca_kv(o, p, "removed" as *u8, rc) 347 p = ca_puts(o, p, "}" as *u8) 348 return p 349} 350 351// THE DISPATCHER: argv[0] is the verb, args at argv[1..]. Returns the JSON byte length in out. 352func ca_dispatch(argc: i64, argv: *i64, out: *u8) -> i64 { 353 if argc < 1 { return ca_help(out, 0) } 354 let v: *u8 = argv[0] as *u8 355 if ca_streq(v, "help" as *u8)==1 { return ca_help(out, 0) } 356 if ca_streq(v, "caps" as *u8)==1 { return ca_help(out, 0) } 357 if ca_streq(v, "match" as *u8)==1 { return ca_v_match(out, 0, argv, argc) } 358 if ca_streq(v, "reconnect" as *u8)==1 { return ca_v_reconnect(out, 0, argv, argc) } 359 if ca_streq(v, "lane" as *u8)==1 { return ca_v_lane(out, 0, argv, argc) } 360 if ca_streq(v, "youth" as *u8)==1 { return ca_v_youth(out, 0, argv, argc) } 361 if ca_streq(v, "wards" as *u8)==1 { return ca_v_wards(out, 0, argv, argc) } 362 if ca_streq(v, "events_rsvp" as *u8)==1 { return ca_v_events(out, 0, argv, argc) } 363 if ca_streq(v, "poll" as *u8)==1 { return ca_v_poll(out, 0, argv, argc) } 364 if ca_streq(v, "group_remove" as *u8)==1 { return ca_v_group(out, 0, argv, argc) } 365 if ca_streq(v, "translate" as *u8)==1 { return ca_v_translate(out, 0, argv, argc) } 366 if ca_streq(v, "graph" as *u8)==1 { return ca_v_graph(out, 0, argv, argc) } 367 return ca_err(out, 0, v, "unknown_verb" as *u8, "call help for the verb catalog" as *u8) 368}