code wiki / _hdl_build / nx_uigen_sitegen.nx
nx_uigen_sitegen.nx source
↩ module page · 127 lines · 5565 B
1// nx_uigen_sitegen.nx -- CLI front door for prompt-to-site: ONE brief -> a linked multi-page site ON DISK.
2// usage: nx_uigen_sitegen <brief> <outdir> <domain> [title] [brand]
3// Thin syscall wrapper over the pure lib nx_uigen_site.nx (which the gate proves): us_plan -> per-page
4// us_emit_page -> outdir/<slug> + sitemap.xml + robots.txt, then ONE machine-readable JSON summary line
5// (agentic/workflow contract; additive fields only, rule 19). Fail-LOUD: any write failure exits nonzero
6// with the path named -- a partial site is never reported as success. Envelope declared in-band.
7// expect_exit: 0 license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_uigen_site.nx"
10import "nx_emit_guard.nx"
11
12// page + path buffer sizes, named once (rule 11)
13const K_SG_PAGEBUF: i64 = 1048576
14const K_SG_PATHBUF: i64 = 1024
15
16func cp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
17func cn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(28); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } while k>0 { k=k-1; sys_write(1,(((t as i64)+k) as *u8),1) } return 0 }
18// join dir + "/" + name into dst (0-terminated)
19func pjoin(dst: *u8, dir: *u8, name: *u8) -> i64 {
20 var o: i64 = 0
21 var i: i64 = 0
22 while dir[i] != (0 as u8) { dst[o] = dir[i]; o = o + 1; i = i + 1 }
23 dst[o] = 47 as u8
24 o = o + 1
25 i = 0
26 while name[i] != (0 as u8) { dst[o] = name[i]; o = o + 1; i = i + 1 }
27 dst[o] = 0 as u8
28 return o
29}
30// write len bytes at buf+base to path; returns 1 ok / 0 fail
31func fput(path: *u8, buf: *u8, base: i64, len: i64) -> i64 {
32 let fd: i64 = sys_openat_wr(path, 420)
33 if fd < 0 { return 0 }
34 let w: i64 = sys_write(fd, (((buf as i64) + base) as *u8), len)
35 sys_close(fd)
36 if w != len { return 0 }
37 return 1
38}
39
40func main(argc: i64, argv: *i64) -> i64 {
41 if argc < 4 {
42 cp("usage: nx_uigen_sitegen <brief> <outdir> <domain> [title] [brand] [reqfile]\n" as *u8)
43 cp(" one written brief -> a linked multi-page site (pages + sitemap.xml + robots.txt) in <outdir>\n" as *u8)
44 cp(" reqfile (optional): '<page>|<key>|<value>' lines of real copy; absent keys keep defaults\n" as *u8)
45 sys_exit(2)
46 return 2
47 }
48 let brief: *u8 = argv[1] as *u8
49 let outdir: *u8 = argv[2] as *u8
50 let domain: *u8 = argv[3] as *u8
51 var title: *u8 = "Generated Site" as *u8
52 if argc > 4 { title = argv[4] as *u8 }
53 var brand: *u8 = title
54 if argc > 5 { brand = argv[5] as *u8 }
55 var req: *u8 = 0 as *u8
56 var reqlines: i64 = 0
57 if argc > 6 {
58 let rlen: *i64 = sys_mmap(8) as *i64
59 let raw: *u8 = sys_read_file(argv[6] as *u8, rlen)
60 if (raw as i64) == 0 {
61 cp("SITEGEN-FAIL cannot read reqfile " as *u8); cp(argv[6] as *u8); cp("\n" as *u8)
62 sys_exit(4)
63 return 4
64 }
65 let rl: i64 = rlen[0]
66 req = sys_mmap(rl + 1)
67 var ci: i64 = 0
68 while ci < rl { req[ci] = raw[ci]; ci = ci + 1 }
69 req[rl] = 0 as u8
70 ci = 0
71 while ci < rl { if req[ci] == (10 as u8) { reqlines = reqlines + 1 } ci = ci + 1 }
72 }
73
74 sys_mkdir(outdir, 493)
75 // guards live in nx_emit_guard (one home, both emitter CLIs) -- an unchecked allocation turns a
76 // generator into a silent no-op that still prints success
77 let buf: *u8 = eg_buf(K_SG_PAGEBUF, "sitegen page buffer" as *u8)
78 let path: *u8 = eg_buf(K_SG_PATHBUF, "sitegen path buffer" as *u8)
79 let plan: *i64 = sys_mmap(64) as *i64
80 let n: i64 = us_plan(brief, plan)
81 var total: i64 = 0
82 var i: i64 = 0
83 while i < n {
84 let len: i64 = us_emit_page_req(buf, 0, brief, title, brand, plan, n, plan[i], req)
85 eg_assert_size(len, "generated page" as *u8)
86 pjoin(path, outdir, us_slug(plan[i]))
87 if fput(path, buf, 0, len) == 0 {
88 cp("SITEGEN-FAIL cannot write " as *u8); cp(path); cp("\n" as *u8)
89 sys_exit(3)
90 return 3
91 }
92 total = total + len
93 i = i + 1
94 }
95 var mlen: i64 = us_sitemap(buf, 0, domain, plan, n)
96 pjoin(path, outdir, "sitemap.xml" as *u8)
97 if fput(path, buf, 0, mlen) == 0 { cp("SITEGEN-FAIL cannot write sitemap\n" as *u8); sys_exit(3); return 3 }
98 total = total + mlen
99 mlen = us_robots(buf, 0, domain)
100 pjoin(path, outdir, "robots.txt" as *u8)
101 if fput(path, buf, 0, mlen) == 0 { cp("SITEGEN-FAIL cannot write robots\n" as *u8); sys_exit(3); return 3 }
102 total = total + mlen
103
104 // POST-WRITE SET ASSERTION: prove the whole set exists before claiming success (the round-18 law)
105 var vi: i64 = 0
106 while vi < n {
107 pjoin(path, outdir, us_slug(plan[vi]))
108 eg_assert_present(path)
109 vi = vi + 1
110 }
111 // machine-readable summary (ONE line JSON; additive-only contract)
112 cp("{\"organ\":\"nx_uigen_sitegen\",\"v\":1,\"pages\":" as *u8); cn(n)
113 cp(",\"slugs\":[" as *u8)
114 i = 0
115 while i < n {
116 if i > 0 { cp("," as *u8) }
117 cp("\"" as *u8); cp(us_slug(plan[i])); cp("\"" as *u8)
118 i = i + 1
119 }
120 cp("],\"hue\":" as *u8); cn(l2_hue(brief))
121 cp(",\"seed\":" as *u8); cn(us_hash(brief))
122 cp(",\"bytes\":" as *u8); cn(total)
123 cp(",\"req_lines\":" as *u8); cn(reqlines)
124 cp(",\"deterministic\":1,\"env\":\"bounded 6-page vocabulary; copy=req-brief-or-default (prose gen=L4); flat basenames; sitemap+robots derived from the same plan\"}\n" as *u8)
125 sys_exit(0)
126 return 0
127}