code wiki / _hdl_build / nx_gen.nx
nx_gen.nx source
↩ module page · 177 lines · 11139 B
1// nx_gen.nx -- the sovereign GEN MCP tool: a thin NAS-side client for the gen orchestrator daemon (loopback :18795).
2// Runs ON THE NAS (invoked by nx_tools_api over /mcp), so it can reach the plain-HTTP loopback daemon that nx_https_get
3// (HTTPS-only) and the login-gated /gen edge cannot -- and returns the daemon's JSON straight through the MCP channel
4// (no root-owned files, no namespace/harness games). Verbs:
5// anatomy <prompt> -> POST /api/anatomy {prompt} = instant deterministic QC (risk/factors/wetfix)
6// recent [n] -> GET /api/recent?n=n = latest gallery cids
7// chat <message> -> POST /api/chat {message} = companion reply (photo msgs also gen)
8// generate <prompt> [count] [WxH] [seed] -> POST /api/generate = render on the swarm GPU + ingest -> {count,ms,cids}
9// img2img <cid> <prompt> [denoise] -> POST /api/img2img = edit an EXISTING gallery cid -> new cid
10// batch <prompt> [count] [WxH] [seed] -> POST /api/batch = async render (edge-timeout-proof) -> job id
11// batchget <jid> -> GET /api/batch/<jid> = poll the async job ({"done":0} while rendering)
12// (2026-08-04: generate/img2img/batch/batchget close the "expose the gen daemon as a full MCP tool" debt --
13// agents/workflows drive the WHOLE gen loop over MCP; the daemon resolves its GPU worker from the swarm SSOT.)
14// license_tier: ORIGINAL expect_exit: 0
15import "nx_syscalls.nx"
16import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
17const K_MAGIC_4194304: i64 = 4194304
18const K_MAGIC_131072: i64 = 131072
19const K_MAGIC_65536: i64 = 65536
20const K_MAGIC_18795: i64 = 18795
21const G_TMO_FAST: i64 = 60
22const G_TMO_RENDER: i64 = 300
23
24func g_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
25func g_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o }
26func g_catn(dst: *u8, off: i64, s: *u8, n: i64) -> i64 { var o: i64=off; var i: i64=0; while i<n {dst[o]=s[i]; o=o+1; i=i+1} return o }
27func g_itoa(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var o: i64=off; var q: i64=k-1; while q>=0{dst[o]=t[q];o=o+1;q=q-1} return o }
28func g_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if (a[i]&0xff)!=(b[i]&0xff) { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 }
29// parse a non-negative integer out of s (ignores non-digits); returns dflt when none found
30func g_atoi(s: *u8, dflt: i64) -> i64 { var v: i64=0; var seen: i64=0; var i: i64=0; while s[i]!=(0 as u8) { let c: i64=s[i]&0xff; if c>=48 { if c<=57 { v=v*10+(c-48); seen=1 } } i=i+1 } if seen==0 { return dflt } return v }
31// JSON-escape s into dst at off (escapes ", \, and control chars -> space)
32func g_jesc(dst: *u8, off: i64, s: *u8) -> i64 {
33 var o: i64=off; var i: i64=0
34 while s[i]!=(0 as u8) {
35 let c: i64 = s[i]&0xff
36 if c==34 { dst[o]=92 as u8; o=o+1; dst[o]=34 as u8; o=o+1 }
37 else { if c==92 { dst[o]=92 as u8; o=o+1; dst[o]=92 as u8; o=o+1 }
38 else { if c<32 { dst[o]=32 as u8; o=o+1 } else { dst[o]=s[i]; o=o+1 } } }
39 i=i+1
40 }
41 return o
42}
43// is s a plain decimal number (digits with at most one dot)? 1=yes -- safe to emit as a raw JSON number
44func g_is_decimal(s: *u8) -> i64 {
45 var i: i64=0; var dots: i64=0; var digits: i64=0
46 while s[i]!=(0 as u8) {
47 let c: i64=s[i]&0xff
48 if c==46 { dots=dots+1 } else { if c<48 { return 0 } if c>57 { return 0 } digits=digits+1 }
49 i=i+1
50 }
51 if digits==0 { return 0 }
52 if dots>1 { return 0 }
53 return 1
54}
55// connect loopback:port, send req[0..reqlen], stream the response BODY (after \r\n\r\n) to stdout. returns 0 ok.
56func g_http(port: i64, req: *u8, reqlen: i64, tmo: i64) -> i64 {
57 let fd: i64 = sys_socket(2, 1, 0)
58 if fd < 0 { sys_write(2, "nx_gen: socket-fail\n" as *u8, 20); return 1 }
59 sys_set_socket_timeout(fd, tmo)
60 let a: *u8 = sys_mmap(16)
61 a[0]=2 as u8; a[1]=0 as u8; a[2]=((port>>8)&0xff) as u8; a[3]=(port&0xff) as u8
62 a[4]=127 as u8; a[5]=0 as u8; a[6]=0 as u8; a[7]=1 as u8
63 var zi: i64=8; while zi<16 { a[zi]=0 as u8; zi=zi+1 }
64 if nx_connect_bounded(fd, a, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); sys_write(2, "nx_gen: daemon unreachable on 127.0.0.1:18795 -- is nx_gen_orchestrator_daemon up?\n" as *u8, 81); return 2 }
65 sys_write(fd, req, reqlen)
66 let resp: *u8 = sys_mmap(K_MAGIC_4194304); var total: i64 = 0; var go: i64 = 1
67 while go == 1 { let r: i64 = sys_read(fd, (resp as i64 + total) as *u8, K_MAGIC_4194304 - total); if r <= 0 { go=0 } else { total=total+r; if total>=K_MAGIC_4194304 { go=0 } } }
68 sys_close(fd)
69 var bs: i64 = 0; var i: i64 = 0
70 while i + 4 <= total { if (resp[i]&0xff)==13 { if (resp[i+1]&0xff)==10 { if (resp[i+2]&0xff)==13 { if (resp[i+3]&0xff)==10 { bs=i+4; i=total } } } } i=i+1 }
71 if bs == 0 { sys_write(1, resp, total) } else { sys_write(1, (resp as i64 + bs) as *u8, total - bs) }
72 sys_write(1, "\n" as *u8, 1)
73 return 0
74}
75// build the {"prompt":..,"count":N,"width":W,"height":H[,"seed":S]} body shared by generate + batch.
76// argv layout: argv[2]=prompt argv[3]=count argv[4]=WxH argv[5]=seed (all optional past prompt).
77func g_render_body(body: *u8, argc: i64, argv: *i64) -> i64 {
78 var prompt: *u8 = "" as *u8
79 if argc >= 3 { prompt = argv[2] as *u8 }
80 var count: i64 = 1
81 if argc >= 4 { count = g_atoi(argv[3] as *u8, 1) }
82 var w: i64 = 512
83 var h: i64 = 512
84 if argc >= 5 {
85 let sz: *u8 = argv[4] as *u8
86 var wi: i64 = 0; var xat: i64 = 0 - 1; var i: i64 = 0
87 while sz[i] != (0 as u8) { if (sz[i]&0xff)==120 { xat=i } i=i+1 }
88 if xat > 0 {
89 var wv: i64=0; wi=0
90 while wi < xat { let c: i64=sz[wi]&0xff; if c>=48 { if c<=57 { wv=wv*10+(c-48) } } wi=wi+1 }
91 let hv: i64 = g_atoi((sz as i64 + xat + 1) as *u8, 0)
92 if wv > 0 { if hv > 0 { w=wv; h=hv } }
93 }
94 }
95 var bo: i64 = g_cat(body, 0, "{\"prompt\":\"" as *u8); bo = g_jesc(body, bo, prompt)
96 bo = g_cat(body, bo, "\",\"count\":" as *u8); bo = g_itoa(body, bo, count)
97 bo = g_cat(body, bo, ",\"width\":" as *u8); bo = g_itoa(body, bo, w)
98 bo = g_cat(body, bo, ",\"height\":" as *u8); bo = g_itoa(body, bo, h)
99 if argc >= 6 { let sv: i64 = g_atoi(argv[5] as *u8, 0 - 1); if sv >= 0 { bo = g_cat(body, bo, ",\"seed\":" as *u8); bo = g_itoa(body, bo, sv) } }
100 bo = g_cat(body, bo, "}" as *u8)
101 return bo
102}
103// assemble a loopback POST with a JSON body into req; returns request length
104func g_post(req: *u8, path: *u8, body: *u8, bo: i64) -> i64 {
105 var o: i64 = g_cat(req, 0, "POST " as *u8)
106 o = g_cat(req, o, path)
107 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
108 o = g_itoa(req, o, bo); o = g_cat(req, o, "\r\n\r\n" as *u8); o = g_catn(req, o, body, bo)
109 return o
110}
111
112func main(argc: i64, argv: *i64) -> i64 {
113 let usage: *u8 = "usage: nx_gen <anatomy <prompt> | recent [n] | chat <message> | generate <prompt> [count] [WxH] [seed] | img2img <cid> <prompt> [denoise] | batch <prompt> [count] [WxH] [seed] | batchget <jid>>\n" as *u8
114 if argc < 2 { sys_write(1, usage, g_slen(usage)); return 1 }
115 let verb: *u8 = argv[1] as *u8
116 let req: *u8 = sys_mmap(K_MAGIC_131072)
117
118 if g_streq(verb, "anatomy" as *u8)==1 {
119 var prompt: *u8 = "" as *u8
120 if argc >= 3 { prompt = argv[2] as *u8 }
121 let body: *u8 = sys_mmap(K_MAGIC_65536); var bo: i64 = g_cat(body, 0, "{\"prompt\":\"" as *u8); bo = g_jesc(body, bo, prompt); bo = g_cat(body, bo, "\"}" as *u8)
122 let o: i64 = g_post(req, "/api/anatomy" as *u8, body, bo)
123 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST)
124 }
125 if g_streq(verb, "chat" as *u8)==1 {
126 var msg: *u8 = "" as *u8
127 if argc >= 3 { msg = argv[2] as *u8 }
128 let body: *u8 = sys_mmap(K_MAGIC_65536); var bo: i64 = g_cat(body, 0, "{\"message\":\"" as *u8); bo = g_jesc(body, bo, msg); bo = g_cat(body, bo, "\"}" as *u8)
129 let o: i64 = g_post(req, "/api/chat" as *u8, body, bo)
130 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER)
131 }
132 if g_streq(verb, "recent" as *u8)==1 {
133 var n: i64 = 12
134 if argc >= 3 { n = g_atoi(argv[2] as *u8, 12) }
135 var o: i64 = g_cat(req, 0, "GET /api/recent?n=" as *u8); o = g_itoa(req, o, n)
136 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8)
137 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST)
138 }
139 if g_streq(verb, "generate" as *u8)==1 {
140 if argc < 3 { sys_write(1, "nx_gen generate needs a prompt: generate <prompt> [count] [WxH] [seed]\n" as *u8, 71); return 1 }
141 let body: *u8 = sys_mmap(K_MAGIC_65536)
142 let bo: i64 = g_render_body(body, argc, argv)
143 let o: i64 = g_post(req, "/api/generate" as *u8, body, bo)
144 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER)
145 }
146 if g_streq(verb, "batch" as *u8)==1 {
147 if argc < 3 { sys_write(1, "nx_gen batch needs a prompt: batch <prompt> [count] [WxH] [seed]\n" as *u8, 65); return 1 }
148 let body: *u8 = sys_mmap(K_MAGIC_65536)
149 let bo: i64 = g_render_body(body, argc, argv)
150 let o: i64 = g_post(req, "/api/batch" as *u8, body, bo)
151 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST)
152 }
153 if g_streq(verb, "batchget" as *u8)==1 {
154 if argc < 3 { sys_write(1, "nx_gen batchget needs a job id from batch\n" as *u8, 42); return 1 }
155 var o: i64 = g_cat(req, 0, "GET /api/batch/" as *u8)
156 let jid: *u8 = argv[2] as *u8
157 var ji: i64 = 0
158 while jid[ji] != (0 as u8) { let c: i64=jid[ji]&0xff; if c>=48 { if c<=57 { req[o]=jid[ji]; o=o+1 } } ji=ji+1 }
159 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8)
160 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST)
161 }
162 if g_streq(verb, "img2img" as *u8)==1 {
163 if argc < 4 { sys_write(1, "nx_gen img2img needs <cid> <prompt> [denoise 0.0-1.0, default 0.6] -- list cids via recent\n" as *u8, 91); return 1 }
164 let body: *u8 = sys_mmap(K_MAGIC_65536)
165 var bo: i64 = g_cat(body, 0, "{\"cid\":\"" as *u8); bo = g_jesc(body, bo, argv[2] as *u8)
166 bo = g_cat(body, bo, "\",\"prompt\":\"" as *u8); bo = g_jesc(body, bo, argv[3] as *u8)
167 bo = g_cat(body, bo, "\",\"denoise\":" as *u8)
168 var dn: *u8 = "0.6" as *u8
169 if argc >= 5 { if g_is_decimal(argv[4] as *u8)==1 { dn = argv[4] as *u8 } }
170 bo = g_cat(body, bo, dn)
171 bo = g_cat(body, bo, "}" as *u8)
172 let o: i64 = g_post(req, "/api/img2img" as *u8, body, bo)
173 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER)
174 }
175 let unk: *u8 = "nx_gen: unknown verb (use anatomy|recent|chat|generate|img2img|batch|batchget)\n" as *u8
176 sys_write(1, unk, g_slen(unk)); return 1
177}