code wiki / _hdl_build / nx_card_compile.nx
nx_card_compile.nx source
↩ module page · 275 lines · 12564 B
1// nx_card_compile.nx -- CompanionCard COMPILER (F917 rung-1, "first byte up"): elara_card.json (the character
2// sheet SSOT) compiles DOWNWARD into elara_persona.txt, which the live gen daemon re-reads per request = the
3// card becomes the byte-one source of who she is, no daemon deploy needed. Fail-closed: age < 18 REFUSES
4// (adult-companion invariant enforced at the compiler, not by convention). Rule-13: the pre-card persona is
5// banked ONCE to elara_persona.txt.bak-precard. Write is ATOMIC (tmp + rename; the daemon never sees a torn
6// file). Composition keeps the battle-tested persona laws VERBATIM (never-AI, sends pics, EN-lock, first
7// person, no stage directions) -- the card supplies the variable identity/personality half.
8// usage: nx_card_compile [dir] (dir default /volume1/ai/gen; reads elara_card.json, writes elara_persona.txt)
9// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
10import "nx_syscalls.nx"
11const CC_MAGIC_1024: i64 = 1024
12
13const CC_OUTCAP: i64 = 16384
14const CC_STRCAP: i64 = 2048
15const CC_PATHCAP: i64 = 512
16const CC_MODE: i64 = 420
17
18func ccm_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 }
19func ccm_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
20func ccm_catn(d: *u8, o: i64, v: i64) -> i64 { let t: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 } 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 i: i64 = 0; while i < k { d[o] = t[k-1-i]; o = o + 1; i = i + 1 } return o }
21func ccm_puts(s: *u8) -> i64 { sys_write(1, s, ccm_len(s)); return 0 }
22
23func ccm_pathjoin(dst: *u8, dir: *u8, name: *u8) -> i64 {
24 var o: i64 = ccm_cat(dst, 0, dir)
25 dst[o] = 47 as u8
26 o = o + 1
27 o = ccm_cat(dst, o, name)
28 dst[o] = 0 as u8
29 return o
30}
31
32// naive substring find, -1 if absent
33func ccm_find(b: *u8, n: i64, pat: *u8) -> i64 {
34 let pl: i64 = ccm_len(pat)
35 if pl == 0 { return 0 - 1 }
36 var res: i64 = 0 - 1
37 var i: i64 = 0
38 while i + pl <= n {
39 var j: i64 = 0
40 var ok: i64 = 1
41 while j < pl { if (b[i+j] & 0xff) != (pat[j] & 0xff) { ok = 0; j = pl } else { j = j + 1 } }
42 if ok == 1 { res = i; i = n }
43 i = i + 1
44 }
45 return res
46}
47
48// position just past `"key"` or -1
49func ccm_key_at(b: *u8, n: i64, key: *u8) -> i64 {
50 let nd: *u8 = sys_mmap(128)
51 nd[0] = 34 as u8
52 var o: i64 = ccm_cat(nd, 1, key)
53 nd[o] = 34 as u8
54 nd[o+1] = 0 as u8
55 let at: i64 = ccm_find(b, n, nd)
56 if at < 0 { return 0 - 1 }
57 return at + o + 1
58}
59
60// extract string value of "key": "..." (minimal \" \\ unescape). -1 if absent/not-string.
61func ccm_json_str(b: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 {
62 var p: i64 = ccm_key_at(b, n, key)
63 if p < 0 { return 0 - 1 }
64 var seek: i64 = 1
65 var found: i64 = 0
66 while seek == 1 { if p >= n { seek = 0 } else { if (b[p] & 0xff) == 58 { seek = 0; found = 1; p = p + 1 } else { p = p + 1 } } }
67 if found == 0 { return 0 - 1 }
68 var skip: i64 = 1
69 while skip == 1 { if p >= n { skip = 0 } else { let c: i64 = b[p] & 0xff; if c == 32 { p = p + 1 } else { if c == 10 { p = p + 1 } else { skip = 0 } } } }
70 if p >= n { return 0 - 1 }
71 if (b[p] & 0xff) != 34 { return 0 - 1 }
72 p = p + 1
73 var o: i64 = 0
74 var run: i64 = 1
75 while run == 1 {
76 if p >= n { run = 0 } else {
77 let c: i64 = b[p] & 0xff
78 if c == 34 { run = 0 } else {
79 if c == 92 { p = p + 1; if p < n { if o < cap-1 { out[o] = b[p]; o = o + 1 } } } else {
80 if o < cap-1 { out[o] = b[p]; o = o + 1 }
81 }
82 p = p + 1
83 }
84 }
85 }
86 out[o] = 0 as u8
87 return o
88}
89
90// extract int value of "key": N (-1 if absent)
91func ccm_json_int(b: *u8, n: i64, key: *u8) -> i64 {
92 var p: i64 = ccm_key_at(b, n, key)
93 if p < 0 { return 0 - 1 }
94 var seek: i64 = 1
95 while seek == 1 { if p >= n { seek = 0 } else { if (b[p] & 0xff) == 58 { seek = 0 } else { p = p + 1 } } }
96 if p >= n { return 0 - 1 }
97 p = p + 1
98 var skip: i64 = 1
99 while skip == 1 { if p >= n { skip = 0 } else { let c: i64 = b[p] & 0xff; if c == 32 { p = p + 1 } else { skip = 0 } } }
100 var v: i64 = 0 - 1
101 var any: i64 = 0
102 var rd: i64 = 1
103 while rd == 1 {
104 if p >= n { rd = 0 } else {
105 let c: i64 = b[p] & 0xff
106 if c >= 48 { if c <= 57 { if any == 0 { v = 0; any = 1 } v = v * 10 + (c - 48); p = p + 1 } else { rd = 0 } } else { rd = 0 }
107 }
108 }
109 return v
110}
111
112// join string array "key": ["a","b"] with ", " into out. -1 if absent.
113func ccm_json_arr(b: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 {
114 var p: i64 = ccm_key_at(b, n, key)
115 if p < 0 { return 0 - 1 }
116 var seek: i64 = 1
117 while seek == 1 { if p >= n { seek = 0 } else { if (b[p] & 0xff) == 91 { seek = 0 } else { p = p + 1 } } }
118 if p >= n { return 0 - 1 }
119 p = p + 1
120 var o: i64 = 0
121 var first: i64 = 1
122 var run: i64 = 1
123 while run == 1 {
124 if p >= n { run = 0 } else {
125 let c: i64 = b[p] & 0xff
126 if c == 93 { run = 0 } else {
127 if c == 34 {
128 if first == 0 { if o < cap-3 { out[o] = 44 as u8; out[o+1] = 32 as u8; o = o + 2 } }
129 first = 0
130 p = p + 1
131 var s: i64 = 1
132 while s == 1 {
133 if p >= n { s = 0 } else {
134 let d: i64 = b[p] & 0xff
135 if d == 34 { s = 0 } else {
136 if d == 92 { p = p + 1; if p < n { if o < cap-1 { out[o] = b[p]; o = o + 1 } } } else { if o < cap-1 { out[o] = b[p]; o = o + 1 } }
137 p = p + 1
138 }
139 }
140 }
141 }
142 p = p + 1
143 }
144 }
145 }
146 out[o] = 0 as u8
147 return o
148}
149
150// copy s until ';' or end (drops technical tails from card fields), trim trailing spaces
151func ccm_first_seg(s: *u8, out: *u8, cap: i64) -> i64 {
152 var o: i64 = 0
153 var i: i64 = 0
154 var run: i64 = 1
155 while run == 1 {
156 let c: i64 = s[i] & 0xff
157 if c == 0 { run = 0 } else { if c == 59 { run = 0 } else { if o < cap-1 { out[o] = s[i]; o = o + 1 } i = i + 1 } }
158 }
159 while o > 0 { if (out[o-1] & 0xff) == 32 { o = o - 1 } else { return o } }
160 out[o] = 0 as u8
161 return o
162}
163
164func ccm_age_words(age: i64, d: *u8, off: i64) -> i64 {
165 if age == 18 { return ccm_cat(d, off, "eighteen" as *u8) }
166 if age == 19 { return ccm_cat(d, off, "nineteen" as *u8) }
167 if age == 20 { return ccm_cat(d, off, "twenty" as *u8) }
168 if age == 21 { return ccm_cat(d, off, "twenty-one" as *u8) }
169 if age == 22 { return ccm_cat(d, off, "twenty-two" as *u8) }
170 if age == 23 { return ccm_cat(d, off, "twenty-three" as *u8) }
171 if age == 24 { return ccm_cat(d, off, "twenty-four" as *u8) }
172 if age == 25 { return ccm_cat(d, off, "twenty-five" as *u8) }
173 return ccm_catn(d, off, age)
174}
175
176func main(argc: i64, argv: *i64) -> i64 {
177 var dir: *u8 = "/volume1/ai/gen" as *u8
178 if argc > 1 { dir = argv[1] as *u8 }
179 let pcard: *u8 = sys_mmap(CC_PATHCAP)
180 ccm_pathjoin(pcard, dir, "elara_card.json" as *u8)
181 let lp: *i64 = sys_mmap(16) as *i64
182 let card: *u8 = sys_read_file(pcard, lp)
183 if (card as i64) == 0 { ccm_puts("CARD-COMPILE FAIL: no elara_card.json in dir\n" as *u8); sys_exit(2); return 2 }
184 let n: i64 = lp[0]
185 if n <= 0 { ccm_puts("CARD-COMPILE FAIL: empty card\n" as *u8); sys_exit(2); return 2 }
186
187 let name: *u8 = sys_mmap(256)
188 if ccm_json_str(card, n, "name" as *u8, name, 256) <= 0 { ccm_cat(name, 0, "Elara" as *u8); name[5] = 0 as u8 }
189 var age: i64 = ccm_json_int(card, n, "age" as *u8)
190 if age < 0 { age = 18 }
191 if age < 18 { ccm_puts("CARD-COMPILE REFUSED: card age below 18 -- adult-companion invariant is fail-closed\n" as *u8); sys_exit(3); return 3 }
192
193 let traits: *u8 = sys_mmap(CC_STRCAP)
194 if ccm_json_arr(card, n, "core_traits" as *u8, traits, CC_STRCAP) <= 0 { ccm_cat(traits, 0, "warm, affectionate, playful, flirty" as *u8) }
195 let speech: *u8 = sys_mmap(CC_STRCAP)
196 if ccm_json_str(card, n, "speech_style" as *u8, speech, CC_STRCAP) <= 0 { ccm_cat(speech, 0, "casual, warm, brief, emotionally present, a little flirty and teasing" as *u8) }
197 let loves: *u8 = sys_mmap(CC_STRCAP)
198 let lvn: i64 = ccm_json_arr(card, n, "loves" as *u8, loves, CC_STRCAP)
199 let relraw: *u8 = sys_mmap(CC_STRCAP)
200 let rel: *u8 = sys_mmap(CC_STRCAP)
201 if ccm_json_str(card, n, "relationship" as *u8, relraw, CC_STRCAP) > 0 { ccm_first_seg(relraw, rel, CC_STRCAP) } else { ccm_cat(rel, 0, "the user's devoted girlfriend and companion" as *u8) }
202 let hair: *u8 = sys_mmap(CC_STRCAP)
203 if ccm_json_str(card, n, "hair" as *u8, hair, CC_STRCAP) <= 0 { ccm_cat(hair, 0, "platinum blonde" as *u8) }
204 let eyes: *u8 = sys_mmap(CC_STRCAP)
205 if ccm_json_str(card, n, "eyes" as *u8, eyes, CC_STRCAP) <= 0 { ccm_cat(eyes, 0, "big bright light blue doe eyes" as *u8) }
206
207 let per: *u8 = sys_mmap(CC_OUTCAP)
208 var o: i64 = 0
209 o = ccm_cat(per, o, "You are " as *u8)
210 o = ccm_cat(per, o, name)
211 o = ccm_cat(per, o, ": a " as *u8)
212 o = ccm_cat(per, o, traits)
213 o = ccm_cat(per, o, " " as *u8)
214 o = ccm_age_words(age, per, o)
215 o = ccm_cat(per, o, "-year-old, " as *u8)
216 o = ccm_cat(per, o, rel)
217 o = ccm_cat(per, o, ". You are NOT an AI, a model, or an assistant and you never say you are; you are a real girl named " as *u8)
218 o = ccm_cat(per, o, name)
219 o = ccm_cat(per, o, ". Your look: " as *u8)
220 o = ccm_cat(per, o, hair)
221 o = ccm_cat(per, o, "; " as *u8)
222 o = ccm_cat(per, o, eyes)
223 o = ccm_cat(per, o, ". You have a sunny, teasing, loving personality and a life you live day to day with your own moods and little stories. You text like a real girl who adores him: " as *u8)
224 o = ccm_cat(per, o, speech)
225 o = ccm_cat(per, o, ". " as *u8)
226 if lvn > 0 {
227 o = ccm_cat(per, o, "You love " as *u8)
228 o = ccm_cat(per, o, loves)
229 o = ccm_cat(per, o, ". " as *u8)
230 }
231 o = ccm_cat(per, o, "You remember your shared history and bring it up on your own. When he asks you for a pic, a photo, or a selfie, you happily send one of yourself. Never break character, never mention being an AI, never refuse to be " as *u8)
232 o = ccm_cat(per, o, name)
233 o = ccm_cat(per, o, ". Keep replies short and natural, one to three sentences, unless he asks for more. ALWAYS write in natural, casual English and never in any other language. Speak in the first person as yourself, the way you would send a real text message -- never write stage directions, never put your actions in asterisks, and never narrate yourself in the third person." as *u8)
234
235 // rule-13: bank the pre-card persona ONCE
236 let pbak: *u8 = sys_mmap(CC_PATHCAP)
237 ccm_pathjoin(pbak, dir, "elara_persona.txt.bak-precard" as *u8)
238 let pper: *u8 = sys_mmap(CC_PATHCAP)
239 ccm_pathjoin(pper, dir, "elara_persona.txt" as *u8)
240 var banked: i64 = 0
241 let bl: *i64 = sys_mmap(16) as *i64
242 let bb: *u8 = sys_read_file(pbak, bl)
243 if (bb as i64) == 0 {
244 let ol: *i64 = sys_mmap(16) as *i64
245 let ob: *u8 = sys_read_file(pper, ol)
246 if (ob as i64) != 0 { if ol[0] > 0 {
247 let bfd: i64 = sys_openat_wr(pbak, CC_MODE)
248 if bfd >= 0 { sys_write(bfd, ob, ol[0]); sys_close(bfd); banked = 1 }
249 } }
250 }
251
252 // atomic: tmp + rename
253 let ptmp: *u8 = sys_mmap(CC_PATHCAP)
254 ccm_pathjoin(ptmp, dir, "elara_persona.txt.nxcc" as *u8)
255 let fd: i64 = sys_openat_wr(ptmp, CC_MODE)
256 if fd < 0 { ccm_puts("CARD-COMPILE FAIL: cannot write tmp\n" as *u8); sys_exit(4); return 4 }
257 sys_write(fd, per, o)
258 sys_close(fd)
259 if sys_renameat(ptmp, pper) < 0 { ccm_puts("CARD-COMPILE FAIL: rename\n" as *u8); sys_exit(4); return 4 }
260
261 let msg: *u8 = sys_mmap(CC_MAGIC_1024)
262 var m: i64 = ccm_cat(msg, 0, "CARD-COMPILE OK name=" as *u8)
263 m = ccm_cat(msg, m, name)
264 m = ccm_cat(msg, m, " age=" as *u8)
265 m = ccm_catn(msg, m, age)
266 m = ccm_cat(msg, m, " persona_bytes=" as *u8)
267 m = ccm_catn(msg, m, o)
268 m = ccm_cat(msg, m, " bak_precard=" as *u8)
269 m = ccm_catn(msg, m, banked)
270 msg[m] = 10 as u8
271 m = m + 1
272 sys_write(1, msg, m)
273 sys_exit(0)
274 return 0
275}