code wiki / (root) / nx_office_serve.nx

nx_office_serve.nx source

↩ module page · 3109 lines · 197925 B

1// nx_office_serve.nx -- NISHI OFFICE v1, the PRODUCT surface over the gated format organs (operator 2026-07-09: 2// "we need to build our own nishi office"). Until now the suite was organs + scattered sample pages; THIS organ is 3// the office: a zero-JS web app where a family member CREATES, EDITS, VERSIONS, PREVIEWS and DOWNLOADS real 4// documents / spreadsheets / decks. Every save = a NEW version (v1,v2,...) -- nothing is ever overwritten; that 5// additive never-lose history is the suite's one measured exceed, now surfaced as the product spine. 6// Engines (forked ELFs, the gated organs): doc = _offc/nx_docx.elf writerich (H/B/I/P + T pipe-cells) ? 7// sheet = _offc/nx_xlsx.elf fromtsv (TSV grid, =SUM/AVERAGE/MIN/MAX/COUNT formulas) ? deck = _offc/nx_pptx.elf 8// fromspec (S title / B bullet). Preview = each organ's own html mode. Honest v1 scope: SPEC editors (plain 9// textarea round-trip), NOT WYSIWYG -- that is the named next rung, along with co-edit (CRDT) and in-app AI. 10// Layout under <root>/: index.txt (append-only name\tkind registry) + <name>/manifest.txt (append-only 11// v<N>\t<kind>\t<usec> lines) + <name>/v<N>/{spec.txt, file.<ext>, preview.html}. 12// PURE CORE (no main): of_handle (request -> response), of_read_req/of_write_all (fd shells), of_selftest (the 13// offline gate body). Consumers: _hdl_build/nx_office_gate (offline gate) + _hdl_build/nx_office_daemon (accept 14// loop, port 8030). Same discipline as nx_relate_serve. license_tier: ORIGINAL 15import "nx_tool_run.nx" 16import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 17import "nx_itoa_lib.nx" // shared integer emitter: nxi_out (fd) and nxi_buf (NUL-free buffer form) 18import "nx_rebac.nx" // the shared ReBAC plane: doc ownership + sharing are rb_ tuples (doc:<name> owner|viewer|editor) 19import "nx_http_client.nx" // loopback POST to our own sovereign LLM seat (:11434) for in-app AI drafting 20 21// office authz store. The office daemon runs as ELDERWESTO (its reconcile cron), NOT root -- so the store must be 22// in the elderwesto-writable cwd (nishihost/officeauthz_*), NOT the root-owned knowledge/status/ (which relate, 23// running as root, created). Tuples live OUTSIDE office/ so they never render as documents. A doc with NO owner 24// tuple stays PUBLIC (legacy/demo -> non-breaking). 25const OF_AUTHZ: *u8 = "officeauthz_" 26const OF_RELATE: *u8 = "knowledge/status/relate_" 27 28func p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 29// MIGRATED to the shared emitter (debt 1785557603): the old body mmapped TWO buffers per call 30// (LSB-first digits + a reverse buffer) and freed neither. nxi_out is the ONE shim, always balanced. 31func pn(v: i64) -> i64 { nxi_out(v); return 0 } 32func of_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 33func of_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 34func of_catc(dst: *u8, off: i64, code: i64) -> i64 { dst[off] = code as u8; return off + 1 } 35// MIGRATED to nxi_buf, the NUL-FREE shared form. The old body mmapped a reverse buffer per call and 36// never freed it -- and this function has 60 CALL SITES, so the leak was per-call in a long-lived 37// server. nxi_buf is MSB-first (allocates nothing) and, unlike ccz_cat_num, writes NO byte past the 38// returned offset -- gate tooth T15 proves that difference by biting. 39func of_catn(dst: *u8, off: i64, v: i64) -> i64 { return nxi_buf(dst, off, v) } 40func of_seq(a: *u8, b: *u8) -> i64 { 41 var i: i64 = 0 42 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 43 if b[i] != (0 as u8) { return 0 } 44 return 1 45} 46func of_starts(s: *u8, pfx: *u8) -> i64 { var i: i64 = 0; while pfx[i] != (0 as u8) { if s[i] != pfx[i] { return 0 } i = i + 1 } return 1 } 47func of_memhas(buf: *u8, n: i64, needle: *u8) -> i64 { 48 let nl: i64 = of_slen(needle) 49 if nl == 0 { return 0 } 50 var i: i64 = 0 51 while i + nl <= n { 52 var k: i64 = 0; var hit: i64 = 1 53 while k < nl { if buf[i+k] != needle[k] { hit = 0; k = nl } else { k = k + 1 } } 54 if hit == 1 { return 1 } 55 i = i + 1 56 } 57 return 0 58} 59// escape & < > while appending a NUL-terminated string (safe for html text and textarea content) 60func of_esc(dst: *u8, off: i64, s: *u8) -> i64 { 61 var o: i64 = off; var i: i64 = 0 62 while s[i] != (0 as u8) { 63 let c: i64 = s[i] as i64 64 if c == 38 { o = of_cat(dst, o, "&amp;" as *u8) } else { 65 if c == 60 { o = of_cat(dst, o, "&lt;" as *u8) } else { 66 if c == 62 { o = of_cat(dst, o, "&gt;" as *u8) } else { dst[o] = s[i]; o = o + 1 } } } 67 i = i + 1 68 } 69 return o 70} 71func of_hexval(c: i64) -> i64 { 72 if c >= 48 { if c <= 57 { return c - 48 } } 73 if c >= 65 { if c <= 70 { return c - 55 } } 74 if c >= 97 { if c <= 102 { return c - 87 } } 75 return 0 76} 77// urldecode form value for key from body (handles + and %XX). proven pattern (nx_relate_serve). 78func of_form_get(body: *u8, blen: i64, key: *u8, out: *u8, cap: i64) -> i64 { 79 let kl: i64 = of_slen(key) 80 var i: i64 = 0 81 while i < blen { 82 var atk: i64 = 0 83 if i == 0 { atk = 1 } else { if body[i-1] == (38 as u8) { atk = 1 } } 84 if atk == 1 { 85 var m: i64 = 1; var j: i64 = 0 86 while j < kl { if i + j >= blen { m = 0; j = kl } else { if body[i+j] != key[j] { m = 0; j = kl } else { j = j + 1 } } } 87 if m == 1 { if i + kl < blen { if body[i+kl] == (61 as u8) { 88 var q: i64 = i + kl + 1; var t: i64 = 0 89 var go: i64 = 1 90 while go == 1 { 91 if q >= blen { go = 0 } else { 92 let c: i64 = body[q] as i64 93 if c == 38 { go = 0 } else { 94 var ch: i64 = c 95 if c == 43 { ch = 32 } 96 if c == 37 { if q + 2 < blen { ch = of_hexval(body[q+1] as i64) * 16 + of_hexval(body[q+2] as i64); q = q + 2 } } 97 if t < cap - 1 { out[t] = ch as u8; t = t + 1 } 98 q = q + 1 99 } 100 } 101 } 102 out[t] = 0 as u8; return t 103 } } } 104 } 105 i = i + 1 106 } 107 out[0] = 0 as u8; return 0 108} 109func of_write_all(fd: i64, buf: *u8, n: i64) -> i64 { 110 var w: i64 = 0 111 while w < n { let k: i64 = sys_write(fd, (buf as i64 + w) as *u8, n - w); if k <= 0 { return 0 - 1 } w = w + k } 112 return 0 113} 114// read ONE full HTTP request: headers until CRLFCRLF plus Content-Length body. proven pattern. 115func of_read_req(fd: i64, buf: *u8, cap: i64) -> i64 { 116 var n: i64 = 0 117 var hdr_end: i64 = 0 - 1 118 var want: i64 = 0 - 1 119 var go: i64 = 1 120 while go == 1 { 121 if n >= cap - 1 { go = 0 } else { 122 let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n) 123 if r <= 0 { go = 0 } else { 124 n = n + r 125 if hdr_end < 0 { 126 var i: i64 = 0 127 while i + 3 < n { 128 if buf[i] == (13 as u8) { if buf[i+1] == (10 as u8) { if buf[i+2] == (13 as u8) { if buf[i+3] == (10 as u8) { hdr_end = i + 4; i = n } } } } 129 i = i + 1 130 } 131 if hdr_end >= 0 { 132 var cl: i64 = 0 - 1 133 let key: *u8 = "Content-Length:" as *u8 134 let kl: i64 = 15 135 var j: i64 = 0 136 while j + kl < hdr_end { 137 var m: i64 = 1 138 var q: i64 = 0 139 while q < kl { if buf[j+q] != key[q] { m = 0; q = kl } else { q = q + 1 } } 140 if m == 1 { 141 var v: i64 = 0 142 var t: i64 = j + kl 143 while t < hdr_end { 144 let c: i64 = buf[t] as i64 145 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 146 if c == 13 { t = hdr_end } 147 t = t + 1 148 } 149 cl = v 150 j = hdr_end 151 } 152 j = j + 1 153 } 154 if cl < 0 { want = hdr_end } else { want = hdr_end + cl } 155 } 156 } 157 if want >= 0 { if n >= want { go = 0 } } 158 } 159 } 160 } 161 return n 162} 163func of_read_file(path: *u8, buf: *u8, cap: i64) -> i64 { 164 let fd: i64 = sys_openat_rd(path) 165 if fd < 0 { return 0 - 1 } 166 var tot: i64 = 0 167 while tot < cap { let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot); if r <= 0 { break } tot = tot + r } 168 sys_close(fd) 169 return tot 170} 171func of_write_file(path: *u8, buf: *u8, n: i64) -> i64 { 172 let fd: i64 = sys_openat_wr(path, 420) 173 if fd < 0 { return 0 - 1 } 174 let rc: i64 = of_write_all(fd, buf, n) 175 sys_close(fd) 176 return rc 177} 178// extract a header value (key like "Origin:") from the header region; trims one leading space; stops at CR. 179func of_hdr_get(req: *u8, reqlen: i64, key: *u8, out: *u8, cap: i64) -> i64 { 180 out[0] = 0 as u8 181 var he: i64 = reqlen 182 var i: i64 = 0 183 while i + 3 < reqlen { 184 if req[i] == (13 as u8) { if req[i+1] == (10 as u8) { if req[i+2] == (13 as u8) { if req[i+3] == (10 as u8) { he = i; i = reqlen } } } } 185 i = i + 1 186 } 187 let kl: i64 = of_slen(key) 188 var j: i64 = 0 189 while j + kl < he { 190 var m: i64 = 1 191 var q: i64 = 0 192 while q < kl { if req[j+q] != key[q] { m = 0; q = kl } else { q = q + 1 } } 193 if m == 1 { 194 var t: i64 = j + kl 195 if req[t] == (32 as u8) { t = t + 1 } 196 var o: i64 = 0 197 while t < he { if req[t] == (13 as u8) { t = he } else { if o < cap - 1 { out[o] = req[t]; o = o + 1 } t = t + 1 } } 198 out[o] = 0 as u8 199 return o 200 } 201 j = j + 1 202 } 203 return 0 204} 205func of_atoi(s: *u8) -> i64 { 206 var v: i64 = 0 207 var i: i64 = 0 208 while s[i] != (0 as u8) { 209 let d: i64 = (s[i] as i64) - 48 210 if d >= 0 { if d <= 9 { v = v * 10 + d } } 211 i = i + 1 212 } 213 return v 214} 215 216// ---- domain: names, kinds, layout ---- 217func of_name_ok(name: *u8) -> i64 { 218 var i: i64 = 0 219 while name[i] != (0 as u8) { 220 let c: i64 = name[i] as i64 221 var ok: i64 = 0 222 if c >= 97 { if c <= 122 { ok = 1 } } 223 if c >= 48 { if c <= 57 { ok = 1 } } 224 if c == 45 { ok = 1 } 225 if ok == 0 { return 0 } 226 i = i + 1 227 } 228 if i < 1 { return 0 } 229 if i > 40 { return 0 } 230 return 1 231} 232func of_kind_idx(kind: *u8) -> i64 { 233 if of_seq(kind, "doc" as *u8) == 1 { return 0 } 234 if of_seq(kind, "sheet" as *u8) == 1 { return 1 } 235 if of_seq(kind, "deck" as *u8) == 1 { return 2 } 236 return 0 - 1 237} 238func of_ext(k: i64) -> *u8 { 239 if k == 0 { return "docx" as *u8 } 240 if k == 1 { return "xlsx" as *u8 } 241 return "pptx" as *u8 242} 243func of_elf(k: i64) -> *u8 { 244 if k == 0 { return "_offc/nx_docx.elf" as *u8 } 245 if k == 1 { return "_offc/nx_xlsx.elf" as *u8 } 246 return "_offc/nx_pptx.elf" as *u8 247} 248func of_ct(k: i64) -> *u8 { 249 if k == 0 { return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" as *u8 } 250 if k == 1 { return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" as *u8 } 251 return "application/vnd.openxmlformats-officedocument.presentationml.presentation" as *u8 252} 253func of_kind_name(k: i64) -> *u8 { 254 if k == 0 { return "doc" as *u8 } 255 if k == 1 { return "sheet" as *u8 } 256 return "deck" as *u8 257} 258// versions so far for <name> (0 = does not exist); kind of the file copied into kindout. 259func of_manifest_count(root: *u8, name: *u8, kindout: *u8, kcap: i64) -> i64 { 260 kindout[0] = 0 as u8 261 let mp: *u8 = sys_mmap(512) 262 var o: i64 = of_cat(mp, 0, root); o = of_cat(mp, o, "/" as *u8); o = of_cat(mp, o, name); o = of_cat(mp, o, "/manifest.txt" as *u8); mp[o] = 0 as u8 263 let buf: *u8 = sys_mmap(65536) 264 let n: i64 = of_read_file(mp, buf, 65536) 265 if n <= 0 { return 0 } 266 var lines: i64 = 0 267 var i: i64 = 0 268 while i < n { if buf[i] == (10 as u8) { lines = lines + 1 } i = i + 1 } 269 // kind = field 2 of the FIRST line (v<N> \t kind \t usec) 270 var t: i64 = 0 271 while t < n { if buf[t] == (9 as u8) { break } t = t + 1 } 272 t = t + 1 273 var ko: i64 = 0 274 while t < n { if buf[t] == (9 as u8) { t = n } else { if buf[t] == (10 as u8) { t = n } else { if ko < kcap - 1 { kindout[ko] = buf[t]; ko = ko + 1 } t = t + 1 } } } 275 kindout[ko] = 0 as u8 276 return lines 277} 278// build "<root>/<name>/v<N>" into out, return offset 279func of_vdir(root: *u8, name: *u8, vn: i64, out: *u8) -> i64 { 280 var o: i64 = of_cat(out, 0, root); o = of_cat(out, o, "/" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "/v" as *u8); o = of_catn(out, o, vn) 281 out[o] = 0 as u8 282 return o 283} 284// append one line to an append-only file (creates if missing) 285func of_append_line(path: *u8, line: *u8, n: i64) -> i64 { 286 let fd: i64 = sys_openat_append(path, 420) 287 if fd < 0 { return 0 - 1 } 288 let rc: i64 = of_write_all(fd, line, n) 289 sys_close(fd) 290 return rc 291} 292// SAVE: validate -> new version dir -> spec -> fork the format organ (artifact) -> fork html (preview) -> 293// register in manifest (+ index on v1). auto=1 (background autosave) tags the manifest line with an ADDITIVE 294// 4th field "auto" -- older parsers read fields 1-3 unchanged. Returns the new version N, or a LOUD negative: 295// -1 invalid name/kind/spec -2 kind mismatch with existing file -3 organ build failed -4 preview failed 296func of_save(root: *u8, base: *u8, name: *u8, kind: *u8, spec: *u8, sl: i64, auto: i64) -> i64 { 297 if of_name_ok(name) == 0 { return 0 - 1 } 298 let k: i64 = of_kind_idx(kind) 299 if k < 0 { return 0 - 1 } 300 if sl <= 0 { return 0 - 1 } 301 if sl > 131072 { return 0 - 1 } 302 sys_mkdir(root, 493) 303 let nd: *u8 = sys_mmap(512) 304 var no: i64 = of_cat(nd, 0, root); no = of_cat(nd, no, "/" as *u8); no = of_cat(nd, no, name); nd[no] = 0 as u8 305 sys_mkdir(nd, 493) 306 let kbuf: *u8 = sys_mmap(64) 307 let cur: i64 = of_manifest_count(root, name, kbuf, 64) 308 if cur > 0 { if of_seq(kbuf, kind) == 0 { return 0 - 2 } } 309 let vn: i64 = cur + 1 310 let vd: *u8 = sys_mmap(512) 311 of_vdir(root, name, vn, vd) 312 sys_mkdir(vd, 493) 313 let specp: *u8 = sys_mmap(600) 314 var so: i64 = of_cat(specp, 0, vd); so = of_cat(specp, so, "/spec.txt" as *u8); specp[so] = 0 as u8 315 if of_write_file(specp, spec, sl) != 0 { return 0 - 3 } 316 let artp: *u8 = sys_mmap(600) 317 let ext: *u8 = of_ext(k) 318 var ao: i64 = of_cat(artp, 0, vd); ao = of_cat(artp, ao, "/file." as *u8); ao = of_cat(artp, ao, ext); artp[ao] = 0 as u8 319 let prevp: *u8 = sys_mmap(600) 320 var po: i64 = of_cat(prevp, 0, vd); po = of_cat(prevp, po, "/preview.html" as *u8); prevp[po] = 0 as u8 321 let dlu: *u8 = sys_mmap(600) 322 var du: i64 = of_cat(dlu, 0, base); du = of_cat(dlu, du, "/file/" as *u8); du = of_cat(dlu, du, name); du = of_cat(dlu, du, "/v" as *u8); du = of_catn(dlu, du, vn); dlu[du] = 0 as u8 323 let elf: *u8 = of_elf(k) 324 let av: *i64 = sys_mmap(8 * 8) as *i64 325 av[0] = elf as i64 326 if k == 0 { av[1] = "writerich" as *u8 as i64; av[2] = artp as i64; av[3] = specp as i64 } else { 327 if k == 1 { av[1] = "fromtsv" as *u8 as i64; av[2] = specp as i64; av[3] = artp as i64 } else { 328 av[1] = "fromspec" as *u8 as i64; av[2] = specp as i64; av[3] = artp as i64 } } 329 av[4] = 0 330 let capbuf: *u8 = sys_mmap(65536) 331 let ol: *i64 = sys_mmap(16) as *i64 332 let rc: i64 = tr_run_capture(elf, av, capbuf, 65536, ol) 333 if rc != 0 { return 0 - 3 } 334 let av2: *i64 = sys_mmap(8 * 8) as *i64 335 av2[0] = elf as i64 336 av2[1] = "html" as *u8 as i64 337 av2[2] = artp as i64 338 av2[3] = prevp as i64 339 av2[4] = dlu as i64 340 av2[5] = 0 341 let rc2: i64 = tr_run_capture(elf, av2, capbuf, 65536, ol) 342 if rc2 != 0 { return 0 - 4 } 343 let mp: *u8 = sys_mmap(600) 344 var mo: i64 = of_cat(mp, 0, nd); mo = of_cat(mp, mo, "/manifest.txt" as *u8); mp[mo] = 0 as u8 345 let line: *u8 = sys_mmap(256) 346 var lo: i64 = of_cat(line, 0, "v" as *u8); lo = of_catn(line, lo, vn); lo = of_catc(line, lo, 9); lo = of_cat(line, lo, kind); lo = of_catc(line, lo, 9); lo = of_catn(line, lo, sys_now_us()) 347 if auto == 1 { lo = of_catc(line, lo, 9); lo = of_cat(line, lo, "auto" as *u8) } 348 lo = of_catc(line, lo, 10) 349 if of_append_line(mp, line, lo) != 0 { return 0 - 3 } 350 if vn == 1 { 351 let ip: *u8 = sys_mmap(600) 352 var io: i64 = of_cat(ip, 0, root); io = of_cat(ip, io, "/index.txt" as *u8); ip[io] = 0 as u8 353 let il: *u8 = sys_mmap(256) 354 var ilo: i64 = of_cat(il, 0, name); ilo = of_catc(il, ilo, 9); ilo = of_cat(il, ilo, kind); ilo = of_catc(il, ilo, 10) 355 of_append_line(ip, il, ilo) 356 } 357 return vn 358} 359 360// ---- pages (zero-JS, sovereign styling, no hash-colors) ---- 361func of_css(out: *u8, off: i64) -> i64 { 362 // CSS custom properties drive light/dark so the JS theme toggle is one class flip (:root.dark / :root.light); 363 // system preference wins unless the user chose. U-axes rung: app shell, card grid, thumbnails, transitions. 364 var o: i64 = off 365 // canonical --nx-color tokens (fleet bar / ui_rubric) with local shorthand ALIASES so every existing 366 // var(--bg)-style usage keeps working; dark redefines only the canon and the aliases follow. 367 o = of_cat(out, o, "<style>:root{--nx-color-bg:rgb(244,246,251);--nx-color-ink:rgb(23,25,35);--nx-color-surface:rgb(255,255,255);--nx-color-line:rgb(224,227,237);--nx-color-muted:rgb(107,112,128);--nx-color-accent:rgb(41,84,164);--nx-radius:12px;--bg:var(--nx-color-bg);--fg:var(--nx-color-ink);--card:var(--nx-color-surface);--line:var(--nx-color-line);--muted:var(--nx-color-muted);--acc:var(--nx-color-accent);--accfg:rgb(255,255,255);--accbg:rgb(234,240,252);--soft:rgb(249,250,253);--sh:rgba(20,28,55,0.07);--sh2:rgba(20,28,55,0.16);--mark:rgb(255,233,148);--good:rgb(21,110,48)}\n" as *u8) 368 o = of_cat(out, o, "@media(prefers-color-scheme:dark){:root:not(.light){--nx-color-bg:rgb(13,14,19);--nx-color-ink:rgb(226,228,238);--nx-color-surface:rgb(21,23,31);--nx-color-line:rgb(42,45,58);--nx-color-muted:rgb(146,150,165);--nx-color-accent:rgb(124,163,235);--accfg:rgb(16,22,38);--accbg:rgb(31,39,60);--soft:rgb(17,19,26);--sh:rgba(0,0,0,0.4);--sh2:rgba(0,0,0,0.55);--mark:rgb(126,100,14);--good:rgb(89,196,124)}}\n" as *u8) 369 o = of_cat(out, o, ":root.dark{--nx-color-bg:rgb(13,14,19);--nx-color-ink:rgb(226,228,238);--nx-color-surface:rgb(21,23,31);--nx-color-line:rgb(42,45,58);--nx-color-muted:rgb(146,150,165);--nx-color-accent:rgb(124,163,235);--accfg:rgb(16,22,38);--accbg:rgb(31,39,60);--soft:rgb(17,19,26);--sh:rgba(0,0,0,0.4);--sh2:rgba(0,0,0,0.55);--mark:rgb(126,100,14);--good:rgb(89,196,124)}\n" as *u8) 370 o = of_cat(out, o, "*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:var(--fg);margin:0;line-height:1.55;font-size:clamp(15px,1vw + 12px,17px)}\n" as *u8) 371 o = of_cat(out, o, "a{color:var(--acc)}h1{margin:0 0 2px;font-size:clamp(20px,2.6vw,26px)}h2{font-size:clamp(15px,1.8vw,18px);margin:22px 0 4px}.sub{color:var(--muted);margin:0 0 14px}\n" as *u8) 372 // fleet-bar accessibility + motion: skip-link, visible keyboard focus, entrance rise (stagger via 373 // nth-child), and a reduced-motion kill-switch. 374 o = of_cat(out, o, ".skip-link{position:absolute;left:-999px;top:0;background:var(--acc);color:rgb(255,255,255);padding:12px 16px;z-index:99;border-radius:0 0 8px 0}.skip-link:focus{left:0}\n" as *u8) 375 o = of_cat(out, o, ":focus-visible{outline:2px solid var(--acc);outline-offset:2px}.top input[type=text]:focus-visible{outline:0}\n" as *u8) 376 o = of_cat(out, o, "@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}main>*{animation:rise .35s ease both}main>*:nth-child(2){animation-delay:.06s}main>*:nth-child(3){animation-delay:.12s}main>*:nth-child(4){animation-delay:.18s}\n" as *u8) 377 o = of_cat(out, o, "@media(prefers-reduced-motion:reduce){main>*{animation:none}.fcard,button,.chips a,.top input[type=text],.thm{transition:none}html{scroll-behavior:auto}}\n" as *u8) 378 o = of_cat(out, o, ".top{position:sticky;top:0;z-index:9;padding:5px 20px;background:var(--card);border-bottom:1px solid var(--line);box-shadow:0 1px 10px var(--sh)}.top nav{display:flex;align-items:center;gap:12px;max-width:1200px;margin:0 auto}\n" as *u8) 379 o = of_cat(out, o, ".logo{width:30px;height:30px;min-width:30px;border-radius:8px;background:linear-gradient(135deg,rgb(64,110,200),rgb(35,66,132))}\n" as *u8) 380 o = of_cat(out, o, "a.bname{display:inline-flex;align-items:center;gap:9px;min-height:44px;font-weight:700;font-size:1.02rem;color:var(--fg);text-decoration:none;white-space:nowrap}.top form{flex:1;max-width:560px;margin:0}\n" as *u8) 381 o = of_cat(out, o, ".top input[type=text]{width:100%;border:1px solid var(--line);border-radius:20px;padding:8px 16px;font-size:.93rem;background:var(--soft);color:var(--fg);transition:border-color .15s,box-shadow .15s}.top input[type=text]:focus{outline:0;border-color:var(--acc);box-shadow:0 0 0 3px var(--accbg)}\n" as *u8) 382 o = of_cat(out, o, ".thm{display:none;margin-left:auto;background:transparent;color:var(--muted);border:1px solid var(--line);border-radius:16px;padding:5px 12px;font-size:.8rem;cursor:pointer;transition:color .15s,border-color .15s}.js .thm{display:block}.thm:hover{color:var(--acc);border-color:var(--acc)}\n" as *u8) 383 o = of_cat(out, o, ".wrap{max-width:1200px;margin:0 auto;padding:16px 20px 44px}\n" as *u8) 384 o = of_cat(out, o, ".card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin:14px 0}\n" as *u8) 385 o = of_cat(out, o, "table{border-collapse:collapse;width:100%}th,td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--line);font-size:.95rem}thead th{color:var(--muted);font-weight:600;font-size:.85rem}\n" as *u8) 386 o = of_cat(out, o, ".k{display:inline-block;padding:1px 10px;border-radius:20px;font-size:.8rem;font-weight:600;color:rgb(255,255,255)}.k0{background:rgb(41,84,164)}.k1{background:rgb(26,127,55)}.k2{background:rgb(178,106,0)}\n" as *u8) 387 o = of_cat(out, o, "textarea{width:100%;min-height:220px;font-family:ui-monospace,Consolas,monospace;font-size:.92rem;border:1px solid var(--line);border-radius:8px;padding:10px;background:var(--soft);color:var(--fg)}\n" as *u8) 388 o = of_cat(out, o, "input[type=text]{border:1px solid var(--line);border-radius:8px;padding:7px 10px;font-size:.95rem;background:var(--soft);color:var(--fg)}\n" as *u8) 389 o = of_cat(out, o, "button{background:var(--acc);color:var(--accfg);border:0;border-radius:8px;padding:9px 18px;font-size:.95rem;font-weight:600;cursor:pointer;transition:filter .15s,transform .1s}button:hover{filter:brightness(1.08)}button:active{transform:scale(.97)}button.ghost{background:var(--soft);color:var(--acc);border:1px solid var(--line);padding:4px 12px;font-size:.82rem}button.ghost:hover{filter:none;border-color:var(--acc)}\n" as *u8) 390 o = of_cat(out, o, ".hint{color:var(--muted);font-size:.85rem;margin-top:6px}.foot{max-width:1200px;margin:26px auto 0;padding:10px 20px 20px;color:var(--muted);font-size:.8rem;border-top:1px solid var(--line)}\n" as *u8) 391 // ---- home: create tiles, files header (chips + sort), card grid with REAL thumbnails, empty states 392 o = of_cat(out, o, ".newrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;margin:10px 0 4px}\n" as *u8) 393 o = of_cat(out, o, ".newt{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px 14px;transition:box-shadow .18s,border-color .18s}.newt:hover{border-color:var(--acc);box-shadow:0 4px 14px var(--sh)}.newt h3{margin:0 0 8px;font-size:.95rem;display:flex;align-items:center;gap:8px}.newt p{margin:8px 0 0}.newt p.row{display:flex;gap:8px}.newt input[type=text]{flex:1;min-width:0}.newt button{padding:7px 14px;font-size:.88rem}\n" as *u8) 394 o = of_cat(out, o, ".fico{width:26px;height:26px;min-width:26px;border-radius:7px;color:rgb(255,255,255);font-weight:700;font-size:.7rem;display:flex;align-items:center;justify-content:center}\n" as *u8) 395 o = of_cat(out, o, ".fhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:20px 0 2px}.fhead h1{margin:0;font-size:clamp(17px,2vw,21px)}.cnt{color:var(--muted);font-size:.85rem;font-weight:400}\n" as *u8) 396 o = of_cat(out, o, ".chips{display:flex;gap:6px;flex-wrap:wrap}.chips a{text-decoration:none;font-size:.82rem;font-weight:600;padding:4px 14px;min-height:44px;display:inline-flex;align-items:center;border-radius:22px;border:1px solid var(--line);color:var(--muted);transition:color .15s,border-color .15s,background .15s}.chips a:hover{color:var(--acc);border-color:var(--acc)}.chips a.on{background:var(--accbg);color:var(--acc);border-color:var(--acc)}\n" as *u8) 397 o = of_cat(out, o, ".sortl{margin-left:auto;display:flex;align-items:center;font-size:.82rem;color:var(--muted)}.sortl a{text-decoration:none;margin-left:4px;color:var(--muted);min-height:44px;display:inline-flex;align-items:center;padding:0 6px}.sortl a.on{font-weight:700;color:var(--acc)}\n" as *u8) 398 o = of_cat(out, o, ".fgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(225px,1fr));gap:14px;margin-top:12px}\n" as *u8) 399 o = of_cat(out, o, ".fcard{display:block;background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden;text-decoration:none;color:var(--fg);transition:box-shadow .18s,transform .18s,border-color .18s}.fcard:hover{transform:translateY(-2px);border-color:var(--acc);box-shadow:0 8px 22px var(--sh2)}\n" as *u8) 400 o = of_cat(out, o, ".thumb{height:128px;overflow:hidden;background:var(--soft);border-bottom:1px solid var(--line);padding:10px 12px;pointer-events:none}\n" as *u8) 401 o = of_cat(out, o, ".tdoc{font-size:.55rem;line-height:1.5;color:var(--fg)}.tdoc .th{font-weight:700;font-size:.72rem;margin:0 0 2px}.tdoc .tb{font-weight:700}.tdoc .ti{font-style:italic}\n" as *u8) 402 o = of_cat(out, o, "table.tsheet{border-collapse:collapse;font-size:.55rem;width:auto}table.tsheet td{border:1px solid var(--line);padding:2px 6px;max-width:64px;overflow:hidden;white-space:nowrap}table.tsheet tr:first-child td{background:var(--accbg);font-weight:700}\n" as *u8) 403 o = of_cat(out, o, ".tslide{border:1px solid var(--line);border-radius:6px;background:var(--card);padding:8px 10px;box-shadow:0 2px 6px var(--sh)}.tslide .tst{font-weight:700;font-size:.66rem;margin-bottom:3px}.tslide .tsb{font-size:.55rem;color:var(--muted)}\n" as *u8) 404 o = of_cat(out, o, ".fmeta{display:flex;align-items:center;gap:9px;padding:10px 12px}.fmeta .nm{min-width:0}.fmeta .nm b{display:block;font-size:.9rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fmeta .nm span{font-size:.76rem;color:var(--muted)}\n" as *u8) 405 o = of_cat(out, o, ".empty{text-align:center;padding:44px 20px;color:var(--muted)}.empty .row{display:flex;gap:10px;justify-content:center;margin-bottom:14px}.empty .fico{width:38px;height:38px;font-size:.95rem;border-radius:10px}.empty h2{margin:0 0 4px;color:var(--fg)}.empty p{margin:0}\n" as *u8) 406 // ---- editor page: crumb, saved stamp, find highlights 407 o = of_cat(out, o, ".stamp{display:inline-block;margin-left:10px;font-size:.78rem;font-weight:600;color:var(--good);background:var(--accbg);border-radius:12px;padding:2px 10px;vertical-align:middle}\n" as *u8) 408 o = of_cat(out, o, "mark.fm{background:var(--mark);color:inherit;border-radius:2px;padding:0 1px}mark.fm.cur{outline:2px solid var(--acc)}\n" as *u8) 409 // ---- visual editor (WYSIWYG surfaces; hidden until app.js flips html.js -> progressive enhancement) 410 o = of_cat(out, o, ".starter{display:none}.toolbar{display:none;flex-wrap:wrap;gap:6px;margin:0 0 10px;align-items:center}.js .toolbar{display:flex}\n" as *u8) 411 o = of_cat(out, o, ".toolbar button{background:var(--accbg);color:var(--acc);padding:5px 11px;font-size:.85rem;font-weight:600}.findw{display:flex;gap:6px;margin-left:auto;align-items:center}.findw input{padding:5px 10px;font-size:.85rem;border-radius:16px;max-width:150px}\n" as *u8) 412 o = of_cat(out, o, ".visual{display:none}.js .visual{display:block}[contenteditable]{outline:0}[contenteditable]:focus-within,[contenteditable]:focus{box-shadow:0 0 0 2px var(--accbg)}\n" as *u8) 413 o = of_cat(out, o, ".doc{border:1px solid var(--line);border-radius:8px;padding:14px 16px;min-height:220px;background:var(--soft)}.doc h2{font-size:1.2rem;margin:.4em 0 .2em}.doc p{margin:.35em 0}\n" as *u8) 414 o = of_cat(out, o, ".doc table.edt{border-collapse:collapse;margin:.5em 0}.doc table.edt td{border:1px solid var(--line);padding:4px 8px;min-width:60px}\n" as *u8) 415 o = of_cat(out, o, "table.sheet{border-collapse:collapse;width:100%}table.sheet td{border:1px solid var(--line);padding:5px 9px;min-width:70px}table.sheet tr:first-child td{background:var(--accbg);font-weight:600}\n" as *u8) 416 o = of_cat(out, o, ".deck{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px}.slide{border:1px solid var(--line);border-radius:8px;padding:12px;overflow:auto;background:var(--soft)}.slide h3{margin:0 0 6px;font-size:1rem}.slide ul{margin:0;padding-left:18px}.slide .tools{margin:8px 0 0}.slide .tools button{background:transparent;color:var(--muted);padding:2px 6px;font-size:.72rem;font-weight:500}\n" as *u8) 417 o = of_cat(out, o, ".rawwrap{margin:12px 0}.rawwrap summary{cursor:pointer;color:var(--muted);font-size:.85rem}button.save{margin-top:4px}\n" as *u8) 418 // wide tables never sideways-scroll the page on a phone -- they scroll inside their own wrapper 419 o = of_cat(out, o, ".tablewrap{overflow-x:auto} 420/* U4 loading skeletons: a shimmer placeholder honestly says work-in-progress, where a blank pane reads as broken. 421 Held to prefers-reduced-motion like every other animation here. */ 422.skel{background:linear-gradient(90deg,var(--soft) 25%,var(--line) 37%,var(--soft) 63%);background-size:400% 100%;animation:shim 1.4s ease infinite;border-radius:6px;color:transparent!important} 423.skel *{visibility:hidden} 424@keyframes shim{0%{background-position:100% 50%}100%{background-position:0 50%}} 425@media(prefers-reduced-motion:reduce){.skel{animation:none}} 426.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n" as *u8) 427 // ---- version diff (sibling lane; rgba overlays read on BOTH themes so no dark block needed) 428 o = of_cat(out, o, ".crumb{font-size:.85rem;margin:2px 0 12px}.crumb a{text-decoration:none}.diff{font-family:ui-monospace,Consolas,monospace;font-size:.85rem;border:1px solid var(--line);border-radius:8px;overflow:auto}.diff>div{padding:2px 10px;white-space:pre-wrap;border-bottom:1px solid var(--line)}.dadd{background:rgba(46,160,84,0.15);color:var(--good)}.ddel{background:rgba(205,70,70,0.13);color:rgb(190,85,85)}.dkeep{color:var(--muted)}\n" as *u8) 429 // ---- responsive: the shell collapses gracefully on phones 430 o = of_cat(out, o, "@media(max-width:640px){.top{padding:8px 12px;gap:8px}a.bname span+span{display:none}.wrap{padding:12px 12px 34px}.fgrid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}.thumb{height:96px}.sortl{margin-left:0}}\n" as *u8) 431 o = of_cat(out, o, "</style>" as *u8) 432 return o 433} 434// escape src[a,b) (XML & < >) while appending -> for emitting substrings without a NUL terminator 435func of_esc_n(out: *u8, off: i64, src: *u8, a: i64, b: i64) -> i64 { 436 var o: i64 = off 437 var i: i64 = a 438 while i < b { 439 let c: i64 = src[i] as i64 440 if c == 38 { o = of_cat(out, o, "&amp;" as *u8) } else { 441 if c == 60 { o = of_cat(out, o, "&lt;" as *u8) } else { 442 if c == 62 { o = of_cat(out, o, "&gt;" as *u8) } else { out[o] = src[i]; o = o + 1 } } } 443 i = i + 1 444 } 445 return o 446} 447// render a doc spec (H/B/I/P/T lines) as an editable block tree (#ed). Consecutive T lines = one table. 448func of_render_doc(out: *u8, off: i64, spec: *u8, sl: i64) -> i64 { 449 var o: i64 = of_cat(out, off, "<div id='ed' class='visual doc' contenteditable='true'>" as *u8) 450 var i: i64 = 0 451 var intable: i64 = 0 452 while i < sl { 453 var e: i64 = i 454 while e < sl { if spec[e] == (10 as u8) { break } e = e + 1 } 455 let c0: i64 = spec[i] as i64 456 var cs: i64 = i 457 if e >= i + 2 { if spec[i+1] == (32 as u8) { cs = i + 2 } } 458 if c0 == 84 { // 'T' table row 459 if intable == 0 { o = of_cat(out, o, "<table class='edt'><tbody>" as *u8); intable = 1 } 460 o = of_cat(out, o, "<tr>" as *u8) 461 var cstart: i64 = cs 462 var k: i64 = cs 463 while k <= e { 464 if k == e { o = of_cat(out, o, "<td>" as *u8); o = of_esc_n(out, o, spec, cstart, k); o = of_cat(out, o, "</td>" as *u8); k = k + 1 } 465 else { if spec[k] == (124 as u8) { o = of_cat(out, o, "<td>" as *u8); o = of_esc_n(out, o, spec, cstart, k); o = of_cat(out, o, "</td>" as *u8); cstart = k + 1; k = k + 1 } else { k = k + 1 } } 466 } 467 o = of_cat(out, o, "</tr>" as *u8) 468 } else { 469 if intable == 1 { o = of_cat(out, o, "</tbody></table>" as *u8); intable = 0 } 470 if c0 == 72 { o = of_cat(out, o, "<h2>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</h2>" as *u8) } 471 else { if c0 == 66 { o = of_cat(out, o, "<p data-k='B'><b>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</b></p>" as *u8) } 472 else { if c0 == 73 { o = of_cat(out, o, "<p data-k='I'><i>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</i></p>" as *u8) } 473 else { o = of_cat(out, o, "<p>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</p>" as *u8) } } } 474 } 475 i = e + 1 476 } 477 if intable == 1 { o = of_cat(out, o, "</tbody></table>" as *u8) } 478 o = of_cat(out, o, "</div>" as *u8) 479 return o 480} 481// render a sheet spec (TSV) as an editable grid (#grid). 482func of_render_sheet(out: *u8, off: i64, spec: *u8, sl: i64) -> i64 { 483 var o: i64 = of_cat(out, off, "<table id='grid' class='visual sheet'><tbody>" as *u8) 484 var i: i64 = 0 485 while i < sl { 486 var e: i64 = i 487 while e < sl { if spec[e] == (10 as u8) { break } e = e + 1 } 488 o = of_cat(out, o, "<tr>" as *u8) 489 var cstart: i64 = i 490 var k: i64 = i 491 while k <= e { 492 if k == e { o = of_cat(out, o, "<td contenteditable='true'>" as *u8); o = of_esc_n(out, o, spec, cstart, k); o = of_cat(out, o, "</td>" as *u8); k = k + 1 } 493 else { if spec[k] == (9 as u8) { o = of_cat(out, o, "<td contenteditable='true'>" as *u8); o = of_esc_n(out, o, spec, cstart, k); o = of_cat(out, o, "</td>" as *u8); cstart = k + 1; k = k + 1 } else { k = k + 1 } } 494 } 495 o = of_cat(out, o, "</tr>" as *u8) 496 i = e + 1 497 } 498 o = of_cat(out, o, "</tbody></table>" as *u8) 499 return o 500} 501// render a deck spec (S title / B bullet) as editable slide cards (#deck). 502func of_render_deck(out: *u8, off: i64, spec: *u8, sl: i64) -> i64 { 503 var o: i64 = of_cat(out, off, "<div id='deck' class='visual deck'>" as *u8) 504 var i: i64 = 0 505 var inslide: i64 = 0 506 while i < sl { 507 var e: i64 = i 508 while e < sl { if spec[e] == (10 as u8) { break } e = e + 1 } 509 let c0: i64 = spec[i] as i64 510 var cs: i64 = i 511 if e >= i + 2 { if spec[i+1] == (32 as u8) { cs = i + 2 } } 512 if c0 == 83 { // 'S' new slide 513 if inslide == 1 { o = of_cat(out, o, "</ul><p class='tools'><button type='button' data-act='bullet'>+ bullet</button> <button type='button' data-act='delslide'>remove slide</button></p></div>" as *u8) } 514 o = of_cat(out, o, "<div class='slide'><h3 contenteditable='true'>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</h3><ul>" as *u8) 515 inslide = 1 516 } else { if c0 == 66 { // 'B' bullet 517 if inslide == 0 { o = of_cat(out, o, "<div class='slide'><h3 contenteditable='true'>Slide</h3><ul>" as *u8); inslide = 1 } 518 o = of_cat(out, o, "<li contenteditable='true'>" as *u8); o = of_esc_n(out, o, spec, cs, e); o = of_cat(out, o, "</li>" as *u8) 519 } } 520 i = e + 1 521 } 522 if inslide == 1 { o = of_cat(out, o, "</ul><p class='tools'><button type='button' data-act='bullet'>+ bullet</button> <button type='button' data-act='delslide'>remove slide</button></p></div>" as *u8) } 523 o = of_cat(out, o, "</div>" as *u8) 524 return o 525} 526// kind-specific editing toolbar (buttons carry data-act -> app.js) + shared undo/redo/find controls. 527func of_toolbar(out: *u8, off: i64, ks: i64) -> i64 { 528 var o: i64 = of_cat(out, off, "<div class='toolbar'>" as *u8) 529 if ks == 0 { o = of_cat(out, o, "<button type='button' data-act='h'>Heading</button><button type='button' data-act='b'>Bold line</button><button type='button' data-act='i'>Italic line</button><button type='button' data-act='p'>Plain</button><button type='button' data-act='table'>+ table</button><button type='button' data-act='trow'>+ row</button>" as *u8) } 530 if ks == 1 { o = of_cat(out, o, "<button type='button' data-act='row'>+ row</button><button type='button' data-act='col'>+ column</button>" as *u8) } 531 if ks == 2 { o = of_cat(out, o, "<button type='button' data-act='slide'>+ slide</button>" as *u8) } 532 o = of_cat(out, o, "<button type='button' data-act='u' title='Undo (Ctrl+Z)'>Undo</button><button type='button' data-act='r' title='Redo (Ctrl+Y)'>Redo</button><span class='findw'><input id='findq' type='text' placeholder='Find in file'><button type='button' data-act='find'>Find</button></span>" as *u8) 533 o = of_cat(out, o, "</div>" as *u8) 534 return o 535} 536// serve the client at /app.js (read from CWD; NishiLang literal traps -> JS MUST live in a file, never inlined). 537// Static client assets (office_app.js, office_sw.js) live beside the SERVICE root, but a gate runs 538// from buildroot/. A bare cwd-relative read therefore resolves for the daemon and 404s for the gate, 539// which is how five teeth (T3c, T12i, T13d, T-U4-b, T-U4-c) failed while the server itself was fine. 540// Resolve through an ORDERED ROOT LIST so the answer does not depend on where the process happened 541// to be launched -- the build-cwd-is-not-run-cwd law, and rule 17's configuration hierarchy applied 542// to a file path. A symlink would have hidden this; it is the consumer that has to resolve. 543// Returns bytes read, or <=0 if no root has the asset. Frees what it takes. 544func of_read_asset(name: *u8, buf: *u8, cap: i64) -> i64 { 545 var n: i64 = of_read_file(name, buf, cap) 546 if n > 0 { return n } 547 let p: *u8 = sys_mmap(1024) 548 var o: i64 = of_cat(p, 0, "../" as *u8) 549 o = of_cat(p, o, name) 550 p[o] = 0 as u8 551 n = of_read_file(p, buf, cap) 552 sys_munmap(p, 1024) 553 return n 554} 555 556func of_serve_js(out: *u8, cap: i64) -> i64 { 557 let fb: *u8 = sys_mmap(131072) 558 let fl: i64 = of_read_asset("office_app.js" as *u8, fb, 131071) 559 if fl <= 0 { return of_err(out, "404 Not Found" as *u8, "app.js missing" as *u8) } 560 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/javascript; charset=utf-8\r\nContent-Length: " as *u8) 561 o = of_catn(out, o, fl) 562 o = of_cat(out, o, "\r\nCache-Control: max-age=300\r\n\r\n" as *u8) 563 var i: i64 = 0 564 while i < fl { if o + i < cap { out[o + i] = fb[i] } i = i + 1 } 565 return o + fl 566} 567// U4 OFFLINE/PWA. The service worker lives in a FILE for the same reason app.js does -- NishiLang literal 568// traps make inlining JS a hazard. It is served at /sw.js so its scope covers the whole office mount; a worker 569// served from a deeper path could not control the app it is meant to make offline-capable. 570func of_serve_sw(out: *u8, cap: i64) -> i64 { 571 let fb: *u8 = sys_mmap(65536) 572 let fl: i64 = of_read_asset("office_sw.js" as *u8, fb, 65535) 573 if fl <= 0 { return of_err(out, "404 Not Found" as *u8, "sw.js missing" as *u8) } 574 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK 575 576Content-Type: application/javascript; charset=utf-8 577 578Service-Worker-Allowed: / 579 580Content-Length: " as *u8) 581 o = of_catn(out, o, fl) 582 o = of_cat(out, o, " 583 584Cache-Control: max-age=60 585 586 587 588" as *u8) 589 var i: i64 = 0 590 while i < fl { if o + i < cap { out[o + i] = fb[i] } i = i + 1 } 591 return o + fl 592} 593// Web app manifest -- installable to a home screen, which is what "offline" means to a family on a phone. 594func of_serve_manifest(out: *u8, base: *u8, cap: i64) -> i64 { 595 let b: *u8 = sys_mmap(4096) 596 var m: i64 = of_cat(b, 0, "{\"name\":\"Nishi Office\",\"short_name\":\"Office\",\"display\":\"standalone\",\"background_color\":\"rgb(244,246,251)\",\"theme_color\":\"rgb(41,84,164)\",\"start_url\":\"" as *u8) 597 m = of_cat(b, m, base) 598 m = of_cat(b, m, "/\"}" as *u8) 599 b[m] = 0 as u8 600 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK 601 602Content-Type: application/manifest+json 603 604Content-Length: " as *u8) 605 o = of_catn(out, o, m) 606 o = of_cat(out, o, " 607 608 609 610" as *u8) 611 o = of_cat(out, o, b) 612 return o 613} 614func of_page_top(out: *u8, off: i64, title: *u8) -> i64 { 615 var o: i64 = off 616 o = of_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<" as *u8) 617 o = of_catc(out, o, 33) 618 o = of_cat(out, o, "DOCTYPE html>\n<html lang=" as *u8) 619 o = of_catc(out, o, 34) 620 o = of_cat(out, o, "en" as *u8) 621 o = of_catc(out, o, 34) 622 o = of_cat(out, o, "><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>" as *u8) 623 o = of_esc(out, o, title) 624 // favicon: inline SVG data URI, percent-encoded (no raw hash/bang -- nx_cc literal law) 625 o = of_cat(out, o, "</title><link rel='icon' href='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 32 32%22%3E%3Crect width=%2232%22 height=%2232%22 rx=%227%22 fill=%22rgb(41,84,164)%22/%3E%3Ctext x=%2216%22 y=%2223%22 font-size=%2218%22 font-weight=%22bold%22 font-family=%22sans-serif%22 fill=%22white%22 text-anchor=%22middle%22%3EN%3C/text%3E%3C/svg%3E'>" as *u8) 626 o = of_css(out, o) 627 o = of_cat(out, o, "</head><body>\n" as *u8) 628 return o 629} 630func of_page_foot(out: *u8, off: i64) -> i64 { 631 var o: i64 = off 632 o = of_cat(out, o, "<p class='foot'>Nishi Office &mdash; sovereign engines nx_docx &middot; nx_xlsx &middot; nx_pptx (gated); every save is a NEW version, history is never lost. Search, thumbnails, diffs and raw-spec editing work with ZERO JS; visual editing, find, undo, shortcuts and autosave light up with it. Co-edit wiring and in-app AI are the named next rungs.</p></body></html>\n" as *u8) 633 return o 634} 635// the app shell: sticky top bar with brand, live search (GET <base>/?q=) and a JS-only theme toggle. 636// q = the current search text (prefilled so the state is visible); pass an empty buffer elsewhere. 637func of_shell_top(out: *u8, off: i64, title: *u8, base: *u8, q: *u8) -> i64 { 638 var o: i64 = of_page_top(out, off, title) 639 // U4 PWA: installable + a theme colour so the OS chrome matches the app instead of flashing white. 640 // Emitted HERE, not in of_page_top: only the APP SHELL is installable. The bare share/error pages have no 641 // base mount and are not an app -- advertising a manifest from them would install a broken scope. 642 o = of_cat(out, o, "<meta name='theme-color' content='rgb(41,84,164)'><link rel='manifest' href='" as *u8) 643 o = of_cat(out, o, base) 644 o = of_cat(out, o, "/manifest.webmanifest'>" as *u8) 645 // skip-to-content for keyboard/screen-reader users (href='#main'; hash composed via catc) 646 o = of_cat(out, o, "<a class='skip-link' href='" as *u8) 647 o = of_catc(out, o, 35) 648 o = of_cat(out, o, "main'>Skip to content</a><header class='top'><nav><a class='bname' href='" as *u8) 649 o = of_cat(out, o, base) 650 o = of_cat(out, o, "/'><svg class='logo' viewBox='0 0 32 32' aria-hidden='true'><rect width='32' height='32' rx='7' fill='rgb(41,84,164)'/><text x='16' y='23' font-size='18' font-weight='bold' fill='white' text-anchor='middle'>N</text></svg><span>Nishi Office</span></a><form method='get' action='" as *u8) 651 o = of_cat(out, o, base) 652 o = of_cat(out, o, "/'><input type='text' name='q' placeholder='Search files' value='" as *u8) 653 o = of_esc(out, o, q) 654 o = of_cat(out, o, "'></form><button type='button' class='thm' data-act='theme'>Theme</button></nav></header>\n" as *u8) 655 return o 656} 657// human "edited ago" from a usec timestamp (0 = unknown) 658func of_ago(out: *u8, off: i64, us: i64) -> i64 { 659 if us <= 0 { return of_cat(out, off, "&mdash;" as *u8) } 660 var d: i64 = (sys_now_us() - us) / 1000000 661 if d < 0 { d = 0 } 662 if d < 90 { return of_cat(out, off, "just now" as *u8) } 663 if d < 5400 { var o: i64 = of_catn(out, off, d / 60); return of_cat(out, o, "m ago" as *u8) } 664 if d < 129600 { var o2: i64 = of_catn(out, off, d / 3600); return of_cat(out, o2, "h ago" as *u8) } 665 var o3: i64 = of_catn(out, off, d / 86400) 666 return of_cat(out, o3, "d ago" as *u8) 667} 668// usec of the LATEST save = field 3 of the last non-empty manifest line (0 if missing) -> recency sort + stamps 669func of_manifest_last_us(root: *u8, name: *u8) -> i64 { 670 let mp: *u8 = sys_mmap(512) 671 var o: i64 = of_cat(mp, 0, root); o = of_cat(mp, o, "/" as *u8); o = of_cat(mp, o, name); o = of_cat(mp, o, "/manifest.txt" as *u8); mp[o] = 0 as u8 672 let buf: *u8 = sys_mmap(65536) 673 let n: i64 = of_read_file(mp, buf, 65536) 674 if n <= 0 { return 0 } 675 var e: i64 = n 676 while e > 0 { if buf[e-1] == (10 as u8) { e = e - 1 } else { break } } 677 if e <= 0 { return 0 } 678 var s: i64 = e 679 while s > 0 { if buf[s-1] == (10 as u8) { break } s = s - 1 } 680 var tabs: i64 = 0 681 var i: i64 = s 682 while i < e { if buf[i] == (9 as u8) { tabs = tabs + 1; i = i + 1; if tabs == 2 { break } } else { i = i + 1 } } 683 if tabs < 2 { return 0 } 684 var v: i64 = 0 685 while i < e { let c: i64 = buf[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } i = i + 1 } 686 return v 687} 688// 1 if the manifest line for v<N> carries the additive 4th field "auto" (a background autosave), else 0 689func of_manifest_is_auto(root: *u8, name: *u8, vn: i64) -> i64 { 690 let mp: *u8 = sys_mmap(512) 691 var o: i64 = of_cat(mp, 0, root); o = of_cat(mp, o, "/" as *u8); o = of_cat(mp, o, name); o = of_cat(mp, o, "/manifest.txt" as *u8); mp[o] = 0 as u8 692 let buf: *u8 = sys_mmap(65536) 693 let n: i64 = of_read_file(mp, buf, 65536) 694 if n <= 0 { return 0 } 695 let pfx: *u8 = sys_mmap(32) 696 var po: i64 = of_cat(pfx, 0, "v" as *u8); po = of_catn(pfx, po, vn); po = of_catc(pfx, po, 9); pfx[po] = 0 as u8 697 var i: i64 = 0 698 while i < n { 699 var m: i64 = 1 700 var k: i64 = 0 701 while pfx[k] != (0 as u8) { 702 if i + k >= n { m = 0; break } 703 if buf[i+k] != pfx[k] { m = 0; break } 704 k = k + 1 705 } 706 var e: i64 = i 707 while e < n { if buf[e] == (10 as u8) { break } e = e + 1 } 708 if m == 1 { 709 var tabs: i64 = 0 710 var j: i64 = i 711 while j < e { 712 if buf[j] == (9 as u8) { 713 tabs = tabs + 1 714 if tabs == 3 { 715 if j + 4 < e { if buf[j+1] == (97 as u8) { if buf[j+2] == (117 as u8) { if buf[j+3] == (116 as u8) { if buf[j+4] == (111 as u8) { return 1 } } } } } 716 return 0 717 } 718 } 719 j = j + 1 720 } 721 return 0 722 } 723 i = e + 1 724 } 725 return 0 726} 727// server-rendered mini preview of the LATEST spec -> the home card thumbnail (real content, zero JS) 728func of_thumb(out: *u8, off: i64, root: *u8, name: *u8, ks: i64, vc: i64) -> i64 { 729 var o: i64 = of_cat(out, off, "<div class='thumb'>" as *u8) 730 let vd: *u8 = sys_mmap(512) 731 of_vdir(root, name, vc, vd) 732 let sp: *u8 = sys_mmap(600) 733 var so: i64 = of_cat(sp, 0, vd); so = of_cat(sp, so, "/spec.txt" as *u8); sp[so] = 0 as u8 734 let buf: *u8 = sys_mmap(600) 735 let sl: i64 = of_read_file(sp, buf, 520) 736 if sl <= 0 { return of_cat(out, o, "<div class='tdoc'>&hellip;</div></div>" as *u8) } 737 if ks == 0 { 738 o = of_cat(out, o, "<div class='tdoc'>" as *u8) 739 var i: i64 = 0 740 var lines: i64 = 0 741 while i < sl { 742 if lines >= 7 { break } 743 var e: i64 = i 744 while e < sl { if buf[e] == (10 as u8) { break } e = e + 1 } 745 let c0: i64 = buf[i] as i64 746 var cs: i64 = i 747 if e >= i + 2 { if buf[i+1] == (32 as u8) { cs = i + 2 } } 748 var ce: i64 = e 749 if ce > cs + 64 { ce = cs + 64 } 750 if c0 == 72 { o = of_cat(out, o, "<div class='th'>" as *u8) } else { 751 if c0 == 66 { o = of_cat(out, o, "<div class='tb'>" as *u8) } else { 752 if c0 == 73 { o = of_cat(out, o, "<div class='ti'>" as *u8) } else { o = of_cat(out, o, "<div>" as *u8) } } } 753 o = of_esc_n(out, o, buf, cs, ce) 754 o = of_cat(out, o, "</div>" as *u8) 755 lines = lines + 1 756 i = e + 1 757 } 758 o = of_cat(out, o, "</div>" as *u8) 759 } 760 if ks == 1 { 761 o = of_cat(out, o, "<table class='tsheet'><tbody>" as *u8) 762 var i2: i64 = 0 763 var rows: i64 = 0 764 while i2 < sl { 765 if rows >= 5 { break } 766 var e2: i64 = i2 767 while e2 < sl { if buf[e2] == (10 as u8) { break } e2 = e2 + 1 } 768 o = of_cat(out, o, "<tr>" as *u8) 769 var cstart: i64 = i2 770 var k: i64 = i2 771 var cells: i64 = 0 772 while k <= e2 { 773 if cells >= 4 { break } 774 var hit: i64 = 0 775 if k == e2 { hit = 1 } else { if buf[k] == (9 as u8) { hit = 1 } } 776 if hit == 1 { 777 var ce2: i64 = k 778 if ce2 > cstart + 14 { ce2 = cstart + 14 } 779 o = of_cat(out, o, "<td>" as *u8); o = of_esc_n(out, o, buf, cstart, ce2); o = of_cat(out, o, "</td>" as *u8) 780 cstart = k + 1 781 cells = cells + 1 782 } 783 k = k + 1 784 } 785 o = of_cat(out, o, "</tr>" as *u8) 786 rows = rows + 1 787 i2 = e2 + 1 788 } 789 o = of_cat(out, o, "</tbody></table>" as *u8) 790 } 791 if ks == 2 { 792 o = of_cat(out, o, "<div class='tslide'>" as *u8) 793 var i3: i64 = 0 794 var started: i64 = 0 795 var bl: i64 = 0 796 while i3 < sl { 797 var e3: i64 = i3 798 while e3 < sl { if buf[e3] == (10 as u8) { break } e3 = e3 + 1 } 799 let c3: i64 = buf[i3] as i64 800 var cs3: i64 = i3 801 if e3 >= i3 + 2 { if buf[i3+1] == (32 as u8) { cs3 = i3 + 2 } } 802 var ce3: i64 = e3 803 if ce3 > cs3 + 56 { ce3 = cs3 + 56 } 804 if c3 == 83 { 805 if started == 1 { break } 806 o = of_cat(out, o, "<div class='tst'>" as *u8); o = of_esc_n(out, o, buf, cs3, ce3); o = of_cat(out, o, "</div>" as *u8) 807 started = 1 808 } else { if c3 == 66 { if bl < 3 { 809 o = of_cat(out, o, "<div class='tsb'>&bull; " as *u8); o = of_esc_n(out, o, buf, cs3, ce3); o = of_cat(out, o, "</div>" as *u8) 810 bl = bl + 1 811 } } } 812 i3 = e3 + 1 813 } 814 o = of_cat(out, o, "</div>" as *u8) 815 } 816 o = of_cat(out, o, "</div>" as *u8) 817 return o 818} 819// lowercase in place (search is case-insensitive; stored names are already lowercase by of_name_ok) 820func of_lower(s: *u8) -> i64 { 821 var i: i64 = 0 822 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 65 { if c <= 90 { s[i] = (c + 32) as u8 } } i = i + 1 } 823 return 0 824} 825// one filter/sort pill: preserves q, sets type (tv, empty = All) and sort (sv) explicitly 826func of_chip(out: *u8, off: i64, base: *u8, q: *u8, tv: *u8, sv: *u8, label: *u8, on: i64) -> i64 { 827 var o: i64 = of_cat(out, off, "<a class='" as *u8) 828 if on == 1 { o = of_cat(out, o, "on" as *u8) } 829 o = of_cat(out, o, "' href='" as *u8) 830 o = of_cat(out, o, base) 831 o = of_cat(out, o, "/?" as *u8) 832 if q[0] != (0 as u8) { o = of_cat(out, o, "q=" as *u8); o = of_esc(out, o, q); o = of_cat(out, o, "&amp;" as *u8) } 833 if tv[0] != (0 as u8) { o = of_cat(out, o, "type=" as *u8); o = of_cat(out, o, tv); o = of_cat(out, o, "&amp;" as *u8) } 834 o = of_cat(out, o, "sort=" as *u8) 835 o = of_cat(out, o, sv) 836 o = of_cat(out, o, "'>" as *u8) 837 o = of_cat(out, o, label) 838 o = of_cat(out, o, "</a>" as *u8) 839 return o 840} 841// first byte offset of needle in buf[0,n), or -1 -- lets the gate assert ORDERING, not just presence 842func of_findpos(buf: *u8, n: i64, needle: *u8) -> i64 { 843 let nl: i64 = of_slen(needle) 844 if nl == 0 { return 0 - 1 } 845 var i: i64 = 0 846 while i + nl <= n { 847 var k: i64 = 0 848 var hit: i64 = 1 849 while k < nl { if buf[i+k] != needle[k] { hit = 0; k = nl } else { k = k + 1 } } 850 if hit == 1 { return i } 851 i = i + 1 852 } 853 return 0 - 1 854} 855// one Start-new tile: pick a name, Create -> lands on the visual editor. The starter spec rides a HIDDEN INPUT 856// (a hidden default value is what type=hidden is FOR -- and unlike a display:none <textarea> it renders NOTHING 857// in every engine incl. Nishi Browser, so the tile shows no leaked spec text; newlines/tabs survive the POST via 858// URL-encoding, which of_form_get decodes). Submitted as-is, works with NO JS; the editor is where you compose. 859// ---- U3 LIBRARY ORGANISATION: starred + folders ------------------------------------------------------ 860// Both are SIDECAR files next to the manifest, exactly like agent.txt: the document spec stays pure content, 861// nothing about the file format changes, and a missing sidecar simply means "not starred / no folder". No 862// migration, no schema version, and an older daemon reading a newer tree just ignores them. 863func of_side_path(root: *u8, name: *u8, leaf: *u8, out: *u8) -> i64 { 864 var o: i64 = of_cat(out, 0, root); o = of_cat(out, o, "/" as *u8); o = of_cat(out, o, name) 865 o = of_cat(out, o, "/" as *u8); o = of_cat(out, o, leaf); out[o] = 0 as u8 866 return o 867} 868func of_starred(root: *u8, name: *u8) -> i64 { 869 let p: *u8 = sys_mmap(512) 870 of_side_path(root, name, "starred" as *u8, p) 871 let b: *u8 = sys_mmap(8) 872 if of_read_file(p, b, 4) > 0 { return 1 } 873 return 0 874} 875// toggle and return the NEW state. Unstar writes "0" rather than deleting: a retired star is still history, 876// and a write we can read back beats a delete we cannot audit. 877// Toggle and return the NEW state. Unstar writes a zeroed byte rather than deleting: a retired star is still 878// history, and a write we can read back beats a delete we cannot audit. 879// NOTE: never pass the "" literal here -- the nx_cc constant pool ALIASES an empty literal onto the NEXT 880// literal in the pool, so the "empty" write would emit someone else's bytes. Use a real zeroed buffer. 881func of_star_toggle(root: *u8, name: *u8) -> i64 { 882 let p: *u8 = sys_mmap(512) 883 of_side_path(root, name, "starred" as *u8, p) 884 if of_starred(root, name) == 1 { 885 let z: *u8 = sys_mmap(8); z[0] = 0 as u8 886 of_write_file(p, z, 0) 887 return 0 888 } 889 of_write_file(p, "1" as *u8, 1) 890 return 1 891} 892func of_folder_get(root: *u8, name: *u8, out: *u8, cap: i64) -> i64 { 893 let p: *u8 = sys_mmap(512) 894 of_side_path(root, name, "folder" as *u8, p) 895 let n: i64 = of_read_file(p, out, cap - 1) 896 if n <= 0 { out[0] = 0 as u8; return 0 } 897 var e: i64 = n 898 var trim: i64 = 1 899 while trim == 1 { 900 if e <= 0 { trim = 0 } else { 901 let c: i64 = out[e-1] as i64 902 if c == 10 { e = e - 1 } else { if c == 13 { e = e - 1 } else { trim = 0 } } 903 } 904 } 905 out[e] = 0 as u8 906 return e 907} 908func of_folder_set(root: *u8, name: *u8, val: *u8) -> i64 { 909 let p: *u8 = sys_mmap(512) 910 of_side_path(root, name, "folder" as *u8, p) 911 return of_write_file(p, val, of_slen(val)) 912} 913// One template card. Same POST /save contract as of_form, so a template is not a special kind of file -- 914// it is an ordinary document that happens to start with useful content, and every downstream feature 915// (versioning, diff, AI, agent, sharing) works on it unchanged. 916func of_tmpl(out: *u8, off: i64, base: *u8, k: i64, icon: *u8, title: *u8, blurb: *u8, spec: *u8) -> i64 { 917 var o: i64 = of_cat(out, off, "<div class='newt'><h3><div class='fico k" as *u8) 918 o = of_catn(out, o, k) 919 o = of_cat(out, o, "'>" as *u8) 920 o = of_cat(out, o, icon) 921 o = of_cat(out, o, "</div>" as *u8) 922 o = of_cat(out, o, title) 923 o = of_cat(out, o, " <span class='cnt'>." as *u8); o = of_cat(out, o, of_ext(k)); o = of_cat(out, o, "</span></h3><form method='post' action='" as *u8) 924 o = of_cat(out, o, base) 925 o = of_cat(out, o, "/save'><input type='hidden' name='kind' value='" as *u8) 926 o = of_cat(out, o, of_kind_name(k)) 927 o = of_cat(out, o, "'><input type='hidden' name='spec' value='" as *u8) 928 o = of_esc(out, o, spec) 929 o = of_cat(out, o, "'><p class='hint' style='margin:0'>" as *u8) 930 o = of_cat(out, o, blurb) 931 o = of_cat(out, o, "</p><p class='row'><input type='text' name='name' placeholder='file-name (a-z 0-9 dash)' required><button type='submit'>Use</button></p></form></div>\n" as *u8) 932 return o 933} 934func of_form(out: *u8, off: i64, base: *u8, k: i64) -> i64 { 935 var o: i64 = off 936 o = of_cat(out, o, "<div class='newt'><h3><div class='fico k" as *u8) 937 o = of_catn(out, o, k) 938 o = of_cat(out, o, "'>" as *u8) 939 if k == 0 { o = of_cat(out, o, "W" as *u8) } 940 if k == 1 { o = of_cat(out, o, "S" as *u8) } 941 if k == 2 { o = of_cat(out, o, "P" as *u8) } 942 o = of_cat(out, o, "</div>New " as *u8) 943 o = of_cat(out, o, of_kind_name(k)) 944 o = of_cat(out, o, " <span class='cnt'>." as *u8); o = of_cat(out, o, of_ext(k)); o = of_cat(out, o, "</span></h3><form method='post' action='" as *u8) 945 o = of_cat(out, o, base) 946 o = of_cat(out, o, "/save'><input type='hidden' name='kind' value='" as *u8) 947 o = of_cat(out, o, of_kind_name(k)) 948 o = of_cat(out, o, "'><input type='hidden' name='spec' value='" as *u8) 949 if k == 0 { o = of_cat(out, o, "H Untitled document\nP Start writing here." as *u8) } 950 if k == 1 { o = of_cat(out, o, "Item\tAmount\nExample\t1" as *u8) } 951 if k == 2 { o = of_cat(out, o, "S Untitled deck\nB First point" as *u8) } 952 o = of_cat(out, o, "'>" as *u8) 953 if k == 0 { o = of_cat(out, o, "<p class='hint' style='margin:0'>Headings, bold, tables. Opens in Word.</p>" as *u8) } 954 if k == 1 { o = of_cat(out, o, "<p class='hint' style='margin:0'>Real formulas (=SUM, =AVERAGE&hellip;). Opens in Excel.</p>" as *u8) } 955 if k == 2 { o = of_cat(out, o, "<p class='hint' style='margin:0'>Titles and bullets. Opens in PowerPoint.</p>" as *u8) } 956 o = of_cat(out, o, "<p class='row'><input type='text' name='name' placeholder='file-name (a-z 0-9 dash)' required><button type='submit'>Create</button></p></form></div>\n" as *u8) 957 return o 958} 959// the home APP SHELL (U-axes rung): search + type chips + recency/name sort + card grid with REAL content 960// thumbnails + onboarding/no-match empty states. All server-rendered -- works with ZERO JS. 961// qs = the raw query string (q=&type=&sort=), form-urlencoded exactly like a POST body. 962func of_home(root: *u8, base: *u8, qs: *u8, out: *u8, cap: i64, azprefix: *u8, me: *u8, authed: i64) -> i64 { 963 let ql: i64 = of_slen(qs) 964 let q: *u8 = sys_mmap(96) 965 let srt: *u8 = sys_mmap(24) 966 let tf: *u8 = sys_mmap(24) 967 of_form_get(qs, ql, "q" as *u8, q, 80) 968 of_form_get(qs, ql, "sort" as *u8, srt, 24) 969 of_form_get(qs, ql, "type" as *u8, tf, 24) 970 let flt: *u8 = sys_mmap(24) 971 let fld: *u8 = sys_mmap(64) 972 of_form_get(qs, ql, "filter" as *u8, flt, 24) 973 of_form_get(qs, ql, "folder" as *u8, fld, 48) 974 // "shared with me" = docs the ReBAC reverse index grants me that I do NOT own. Answered from the existing 975 // index rather than a second store, so sharing and the library view can never disagree. 976 let shd: *i64 = sys_mmap(8 * 64) as *i64 977 let own: *i64 = sys_mmap(8 * 64) as *i64 978 var nshd: i64 = 0 979 var nown: i64 = 0 980 if of_seq(flt, "shared" as *u8) == 1 { if authed == 1 { 981 nshd = of_my_docs(azprefix, me, shd, 64) 982 nown = of_collect_docs(azprefix, me, "owner" as *u8, own, 0, 64) 983 } } 984 of_lower(q) 985 var o: i64 = of_shell_top(out, 0, "Nishi Office" as *u8, base, q) 986 o = of_cat(out, o, "<main id='main' class='wrap'>" as *u8) 987 // collect entries from the append-only registry, filtered by type chip + search text 988 let idx: *u8 = sys_mmap(512) 989 var io: i64 = of_cat(idx, 0, root); io = of_cat(idx, io, "/index.txt" as *u8); idx[io] = 0 as u8 990 let ib: *u8 = sys_mmap(65536) 991 let n: i64 = of_read_file(idx, ib, 65536) 992 let names: *u8 = sys_mmap(256 * 64) 993 let kidx: *i64 = sys_mmap(256 * 8) as *i64 994 let vcs: *i64 = sys_mmap(256 * 8) as *i64 995 let uss: *i64 = sys_mmap(256 * 8) as *i64 996 let ord: *i64 = sys_mmap(256 * 8) as *i64 997 var total: i64 = 0 998 var ne: i64 = 0 999 let nm: *u8 = sys_mmap(64) 1000 let kd: *u8 = sys_mmap(32) 1001 let kb: *u8 = sys_mmap(64) 1002 var i: i64 = 0 1003 while i < n { 1004 var t: i64 = 0 1005 while i < n { if ib[i] == (9 as u8) { i = i + 1; break } if t < 63 { nm[t] = ib[i]; t = t + 1 } i = i + 1 } 1006 nm[t] = 0 as u8 1007 var t2: i64 = 0 1008 while i < n { if ib[i] == (10 as u8) { i = i + 1; break } if t2 < 31 { kd[t2] = ib[i]; t2 = t2 + 1 } i = i + 1 } 1009 kd[t2] = 0 as u8 1010 if t > 0 { 1011 total = total + 1 1012 var keep: i64 = 1 1013 if tf[0] != (0 as u8) { if of_seq(tf, kd) == 0 { keep = 0 } } 1014 if keep == 1 { if q[0] != (0 as u8) { if of_memhas(nm, of_slen(nm), q) == 0 { keep = 0 } } } 1015 if keep == 1 { if of_seq(flt, "starred" as *u8) == 1 { if of_starred(root, nm) == 0 { keep = 0 } } } 1016 if keep == 1 { if fld[0] != (0 as u8) { 1017 let fbuf: *u8 = sys_mmap(64) 1018 of_folder_get(root, nm, fbuf, 64) 1019 if of_seq(fbuf, fld) == 0 { keep = 0 } 1020 } } 1021 if keep == 1 { if of_seq(flt, "shared" as *u8) == 1 { 1022 var mine: i64 = 0 1023 var si: i64 = 0 1024 while si < nshd { if of_seq(shd[si] as *u8, nm) == 1 { mine = 1; si = nshd } else { si = si + 1 } } 1025 if mine == 0 { keep = 0 } 1026 if mine == 1 { 1027 var owned: i64 = 0 1028 var oi: i64 = 0 1029 while oi < nown { if of_seq(own[oi] as *u8, nm) == 1 { owned = 1; oi = nown } else { oi = oi + 1 } } 1030 if owned == 1 { keep = 0 } 1031 } 1032 } } 1033 if keep == 1 { if ne < 256 { 1034 let ki: i64 = of_kind_idx(kd) 1035 var ks: i64 = ki 1036 if ks < 0 { ks = 0 } 1037 var w: i64 = 0 1038 while nm[w] != (0 as u8) { names[ne * 64 + w] = nm[w]; w = w + 1 } 1039 names[ne * 64 + w] = 0 as u8 1040 kidx[ne] = ks 1041 vcs[ne] = of_manifest_count(root, nm, kb, 64) 1042 uss[ne] = of_manifest_last_us(root, nm) 1043 ord[ne] = ne 1044 ne = ne + 1 1045 } } 1046 } 1047 } 1048 // order: default = most recently edited first (Docs-style); sort=name = alphabetical 1049 var by_name: i64 = 0 1050 if of_seq(srt, "name" as *u8) == 1 { by_name = 1 } 1051 var a: i64 = 0 1052 while a < ne { 1053 var b: i64 = a + 1 1054 while b < ne { 1055 var sw: i64 = 0 1056 if by_name == 1 { 1057 let pa: *u8 = (names as i64 + ord[a] * 64) as *u8 1058 let pb: *u8 = (names as i64 + ord[b] * 64) as *u8 1059 var ci: i64 = 0 1060 var cmp: i64 = 0 1061 while cmp == 0 { 1062 let ca: i64 = pa[ci] as i64 1063 let cb: i64 = pb[ci] as i64 1064 if ca < cb { cmp = 0 - 1 } else { if ca > cb { cmp = 1 } else { if ca == 0 { cmp = 2 } else { ci = ci + 1 } } } 1065 } 1066 if cmp == 1 { sw = 1 } 1067 } else { 1068 if uss[ord[b]] > uss[ord[a]] { sw = 1 } 1069 } 1070 if sw == 1 { let tmp: i64 = ord[a]; ord[a] = ord[b]; ord[b] = tmp } 1071 b = b + 1 1072 } 1073 a = a + 1 1074 } 1075 // first-run onboarding: nothing exists yet 1076 if total == 0 { 1077 o = of_cat(out, o, "<div class='empty'><div class='row'><div class='fico k0'>W</div><div class='fico k1'>S</div><div class='fico k2'>P</div></div><h1>Create your first document</h1><p>Documents, spreadsheets and decks &mdash; created, versioned and downloadable, all on our own stack. Pick a name below to start.</p></div><div class='newrow'>" as *u8) 1078 o = of_form(out, o, base, 0) 1079 o = of_form(out, o, base, 1) 1080 o = of_form(out, o, base, 2) 1081 o = of_cat(out, o, "</div></main>" as *u8) 1082 o = of_page_foot(out, o) 1083 return o 1084 } 1085 o = of_cat(out, o, "<h2>Start new</h2><div class='newrow'>" as *u8) 1086 o = of_form(out, o, base, 0) 1087 o = of_form(out, o, base, 1) 1088 o = of_form(out, o, base, 2) 1089 o = of_cat(out, o, "</div>" as *u8) 1090 // ---- TEMPLATES GALLERY (U3): a blank page is the worst onboarding a suite can offer. Every template is a 1091 // REAL working spec -- the budget carries live =SUM and a #CF rule, the agenda a real table -- so opening one 1092 // demonstrates the engine instead of describing it. Zero JS: each card is a plain POST to the same /save. 1093 o = of_cat(out, o, "<h2>Templates</h2><p class='hint' style='margin:0 0 6px'>Start from something real rather than a blank page. Each one opens as a normal file you can edit, version and download.</p><div class='newrow'>" as *u8) 1094 o = of_tmpl(out, o, base, 0, "W", "Letter", "A dated letter with greeting and sign-off.", "H Letter\nP Dear friend,\nP Write your message here.\nP Warm regards,\nP The Nishi family") 1095 o = of_tmpl(out, o, base, 0, "W", "Meeting agenda", "Heading, attendees and a real table.", "H Meeting Agenda\nB Attendees\nP Add names here.\nT Time|Item|Lead\nT 9:00|Welcome|Host\nT 9:30|Main topic|Team\nP Notes and actions below.") 1096 o = of_tmpl(out, o, base, 1, "S", "Monthly budget", "Live =SUM plus a conditional-format rule.", "Item\tAmount\nGroceries\t400\nUtilities\t180\nTotal\t=SUM(B2:B3)\n#CF B2:B3 greaterThan 300") 1097 o = of_tmpl(out, o, base, 2, "P", "Project update", "Title and bullet slides ready to present.", "S Project Update\nB Where we are\nB What changed\nS Next steps\nB Owner and date") 1098 o = of_cat(out, o, "</div>" as *u8) 1099 // files header: count + type chips + sort links (every control is a plain link -- zero JS) 1100 o = of_cat(out, o, "<div class='fhead'><h1>Files</h1><span class='cnt'>" as *u8) 1101 o = of_catn(out, o, ne) 1102 if ne != total { o = of_cat(out, o, " of " as *u8); o = of_catn(out, o, total) } 1103 o = of_cat(out, o, "</span><span class='chips'>" as *u8) 1104 // U3 chips: Starred and Shared-with-me sit alongside the type chips. Shared is shown only when a caller is 1105 // actually signed in -- an always-visible control that can only ever be empty is a lie about the product. 1106 o = of_cat(out, o, "<a class='" as *u8) 1107 if of_seq(flt, "starred" as *u8) == 1 { o = of_cat(out, o, "on" as *u8) } 1108 o = of_cat(out, o, "' href='" as *u8); o = of_cat(out, o, base) 1109 o = of_cat(out, o, "/?filter=starred'>&starf; Starred</a>" as *u8) 1110 if authed == 1 { 1111 o = of_cat(out, o, "<a class='" as *u8) 1112 if of_seq(flt, "shared" as *u8) == 1 { o = of_cat(out, o, "on" as *u8) } 1113 o = of_cat(out, o, "' href='" as *u8); o = of_cat(out, o, base) 1114 o = of_cat(out, o, "/?filter=shared'>Shared with me</a>" as *u8) 1115 } 1116 let e0: *u8 = sys_mmap(4) 1117 e0[0] = 0 as u8 1118 var srtv: *u8 = "recent" as *u8 1119 if by_name == 1 { srtv = "name" as *u8 } 1120 var all_on: i64 = 0 1121 if tf[0] == (0 as u8) { all_on = 1 } 1122 o = of_chip(out, o, base, q, e0, srtv, "All" as *u8, all_on) 1123 o = of_chip(out, o, base, q, "doc" as *u8, srtv, "Docs" as *u8, of_seq(tf, "doc" as *u8)) 1124 o = of_chip(out, o, base, q, "sheet" as *u8, srtv, "Sheets" as *u8, of_seq(tf, "sheet" as *u8)) 1125 o = of_chip(out, o, base, q, "deck" as *u8, srtv, "Decks" as *u8, of_seq(tf, "deck" as *u8)) 1126 o = of_cat(out, o, "</span><span class='sortl'>Sort:" as *u8) 1127 o = of_chip(out, o, base, q, tf, "recent" as *u8, "Recent" as *u8, 1 - by_name) 1128 o = of_chip(out, o, base, q, tf, "name" as *u8, "Name" as *u8, by_name) 1129 o = of_cat(out, o, "</span></div>" as *u8) 1130 if ne == 0 { 1131 // no-match empty state (search/filter found nothing) 1132 o = of_cat(out, o, "<div class='empty'><h2>No files match</h2><p>" as *u8) 1133 if q[0] != (0 as u8) { o = of_cat(out, o, "Nothing named like &quot;" as *u8); o = of_esc(out, o, q); o = of_cat(out, o, "&quot;. " as *u8) } 1134 o = of_cat(out, o, "<a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/'>Clear search &amp; filters</a></p></div>" as *u8) 1135 } else { 1136 o = of_cat(out, o, "<div class='fgrid'>" as *u8) 1137 var r: i64 = 0 1138 while r < ne { 1139 let ei: i64 = ord[r] 1140 let pnm: *u8 = (names as i64 + ei * 64) as *u8 1141 o = of_cat(out, o, "<a class='fcard' href='" as *u8) 1142 o = of_cat(out, o, base); o = of_cat(out, o, "/doc/" as *u8); o = of_cat(out, o, pnm) 1143 o = of_cat(out, o, "'>" as *u8) 1144 // thumbnails for the first 60 cards keep the page bounded; the rest stay plain cards 1145 if r < 60 { o = of_thumb(out, o, root, pnm, kidx[ei], vcs[ei]) } 1146 o = of_cat(out, o, "<div class='fmeta'><div class='fico k" as *u8) 1147 o = of_catn(out, o, kidx[ei]) 1148 o = of_cat(out, o, "'>" as *u8) 1149 if kidx[ei] == 0 { o = of_cat(out, o, "W" as *u8) } 1150 if kidx[ei] == 1 { o = of_cat(out, o, "S" as *u8) } 1151 if kidx[ei] == 2 { o = of_cat(out, o, "P" as *u8) } 1152 o = of_cat(out, o, "</div><span class='nm'><b>" as *u8) 1153 o = of_esc(out, o, pnm) 1154 o = of_cat(out, o, "</b><span>v" as *u8) 1155 o = of_catn(out, o, vcs[ei]) 1156 o = of_cat(out, o, " &middot; " as *u8) 1157 o = of_ago(out, o, uss[ei]) 1158 o = of_cat(out, o, "</span></span></div></a>" as *u8) 1159 r = r + 1 1160 } 1161 o = of_cat(out, o, "</div>" as *u8) 1162 if ne == 256 { o = of_cat(out, o, "<p class='hint'>Showing the first 256 files.</p>" as *u8) } 1163 } 1164 o = of_cat(out, o, "</main>" as *u8) 1165 o = of_page_foot(out, o) 1166 return o 1167} 1168// ---- version DIFF (leverages the never-lose store: every version's spec.txt is on disk) ---- 1169func of_lineeq(a: *u8, b: *u8) -> i64 { 1170 var i: i64 = 0 1171 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 1172 if b[i] != (0 as u8) { return 0 } 1173 return 1 1174} 1175// split buf[0,n) into NUL-terminated lines in place; store start pointers in lp[]; return count (<=maxl). Caller sets buf[n]=0. 1176func of_split_lines(buf: *u8, n: i64, lp: *i64, maxl: i64) -> i64 { 1177 var c: i64 = 0 1178 var i: i64 = 0 1179 while i < n { 1180 if c >= maxl { return c } 1181 lp[c] = (buf as i64) + i 1182 c = c + 1 1183 while i < n { if buf[i] == (10 as u8) { break } i = i + 1 } 1184 if i < n { buf[i] = 0 as u8; i = i + 1 } 1185 } 1186 return c 1187} 1188func of_read_version_spec(root: *u8, name: *u8, vn: i64, buf: *u8, cap: i64) -> i64 { 1189 let vd: *u8 = sys_mmap(512) 1190 of_vdir(root, name, vn, vd) 1191 let sp: *u8 = sys_mmap(600) 1192 var so: i64 = of_cat(sp, 0, vd); so = of_cat(sp, so, "/spec.txt" as *u8); sp[so] = 0 as u8 1193 let n: i64 = of_read_file(sp, buf, cap) 1194 if n > 0 { buf[n] = 0 as u8; return n } 1195 buf[0] = 0 as u8; return 0 1196} 1197// render an LCS line-diff of specA vs specB into out. added=in B not A (green), removed=in A not B (red). 1198func of_render_diff(out: *u8, off: i64, bufa: *u8, na_bytes: i64, bufb: *u8, nb_bytes: i64) -> i64 { 1199 let MAXL: i64 = 400 1200 let la: *i64 = sys_mmap(MAXL * 8) as *i64 1201 let lb: *i64 = sys_mmap(MAXL * 8) as *i64 1202 let na: i64 = of_split_lines(bufa, na_bytes, la, MAXL) 1203 let nb: i64 = of_split_lines(bufb, nb_bytes, lb, MAXL) 1204 let W: i64 = nb + 1 1205 let dp: *i64 = sys_mmap((na + 1) * (nb + 1) * 8) as *i64 1206 var i: i64 = na 1207 while i >= 0 { 1208 var j: i64 = nb 1209 while j >= 0 { 1210 if i == na { dp[i*W + j] = 0 } else { if j == nb { dp[i*W + j] = 0 } else { 1211 if of_lineeq(la[i] as *u8, lb[j] as *u8) == 1 { dp[i*W + j] = dp[(i+1)*W + (j+1)] + 1 } 1212 else { let d1: i64 = dp[(i+1)*W + j]; let d2: i64 = dp[i*W + (j+1)]; if d1 >= d2 { dp[i*W + j] = d1 } else { dp[i*W + j] = d2 } } 1213 } } 1214 j = j - 1 1215 } 1216 i = i - 1 1217 } 1218 var o: i64 = of_cat(out, off, "<div class='diff'>" as *u8) 1219 var add: i64 = 0; var del: i64 = 0 1220 var a: i64 = 0; var b: i64 = 0 1221 var go: i64 = 1 1222 while go == 1 { 1223 if a < na { if b < nb { 1224 if of_lineeq(la[a] as *u8, lb[b] as *u8) == 1 { 1225 o = of_cat(out, o, "<div class='dkeep'>&nbsp;&nbsp;" as *u8); o = of_esc(out, o, la[a] as *u8); o = of_cat(out, o, "</div>" as *u8); a = a + 1; b = b + 1 1226 } else { if dp[(a+1)*W + b] >= dp[a*W + (b+1)] { 1227 o = of_cat(out, o, "<div class='ddel'>&minus;&nbsp;" as *u8); o = of_esc(out, o, la[a] as *u8); o = of_cat(out, o, "</div>" as *u8); a = a + 1; del = del + 1 1228 } else { 1229 o = of_cat(out, o, "<div class='dadd'>+&nbsp;" as *u8); o = of_esc(out, o, lb[b] as *u8); o = of_cat(out, o, "</div>" as *u8); b = b + 1; add = add + 1 1230 } } 1231 } else { 1232 o = of_cat(out, o, "<div class='ddel'>&minus;&nbsp;" as *u8); o = of_esc(out, o, la[a] as *u8); o = of_cat(out, o, "</div>" as *u8); a = a + 1; del = del + 1 1233 } } else { if b < nb { 1234 o = of_cat(out, o, "<div class='dadd'>+&nbsp;" as *u8); o = of_esc(out, o, lb[b] as *u8); o = of_cat(out, o, "</div>" as *u8); b = b + 1; add = add + 1 1235 } else { go = 0 } } 1236 } 1237 o = of_cat(out, o, "</div><p class='sub' style='margin-top:8px'><b>" as *u8); o = of_catn(out, o, add); o = of_cat(out, o, "</b> line(s) added, <b>" as *u8); o = of_catn(out, o, del); o = of_cat(out, o, "</b> removed.</p>" as *u8) 1238 return o 1239} 1240func of_diffpage(root: *u8, base: *u8, name: *u8, va: i64, vb: i64, out: *u8, cap: i64) -> i64 { 1241 let kb: *u8 = sys_mmap(64) 1242 let vc: i64 = of_manifest_count(root, name, kb, 64) 1243 if vc <= 0 { return 0 - 1 } 1244 if va < 1 { return 0 - 1 } 1245 if vb < 1 { return 0 - 1 } 1246 if va > vc { return 0 - 1 } 1247 if vb > vc { return 0 - 1 } 1248 let ba: *u8 = sys_mmap(131072) 1249 let bb: *u8 = sys_mmap(131072) 1250 let nab: i64 = of_read_version_spec(root, name, va, ba, 131071) 1251 let nbb: i64 = of_read_version_spec(root, name, vb, bb, 131071) 1252 let eqd: *u8 = sys_mmap(4) 1253 eqd[0] = 0 as u8 1254 var o: i64 = of_shell_top(out, 0, name, base, eqd) 1255 o = of_cat(out, o, "<main id='main' class='wrap'><p class='crumb'><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/doc/" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "'>&larr; " as *u8); o = of_esc(out, o, name); o = of_cat(out, o, "</a></p><h1>Changes: v" as *u8) 1256 o = of_catn(out, o, va); o = of_cat(out, o, " &rarr; v" as *u8); o = of_catn(out, o, vb) 1257 o = of_cat(out, o, "</h1><p class='sub'>Every version is kept forever &mdash; this compares the two specs line by line.</p>\n<div class='card'>" as *u8) 1258 o = of_render_diff(out, o, ba, nab, bb, nbb) 1259 o = of_cat(out, o, "</div></main>\n" as *u8) 1260 o = of_page_foot(out, o) 1261 return o 1262} 1263func of_docpage(root: *u8, base: *u8, name: *u8, out: *u8, cap: i64) -> i64 { 1264 let kb: *u8 = sys_mmap(64) 1265 let vc: i64 = of_manifest_count(root, name, kb, 64) 1266 if vc <= 0 { return 0 - 1 } 1267 let ki: i64 = of_kind_idx(kb) 1268 var ks: i64 = ki 1269 if ks < 0 { ks = 0 } 1270 let eq: *u8 = sys_mmap(4) 1271 eq[0] = 0 as u8 1272 var o: i64 = of_shell_top(out, 0, name, base, eq) 1273 o = of_cat(out, o, "<main id='main' class='wrap'><p class='crumb'><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/'>&larr; All files</a></p><h1>" as *u8) 1274 o = of_esc(out, o, name) 1275 o = of_cat(out, o, " <span class='k k" as *u8); o = of_catn(out, o, ks); o = of_cat(out, o, "'>" as *u8); o = of_esc(out, o, kb) 1276 o = of_cat(out, o, "</span> <span class='stamp'>Saved v" as *u8) 1277 o = of_catn(out, o, vc) 1278 o = of_cat(out, o, " &middot; " as *u8) 1279 o = of_ago(out, o, of_manifest_last_us(root, name)) 1280 o = of_cat(out, o, "</span></h1><p class='sub'>" as *u8) 1281 o = of_catn(out, o, vc) 1282 o = of_cat(out, o, " version(s), additive &mdash; saving creates v" as *u8) 1283 o = of_catn(out, o, vc + 1) 1284 o = of_cat(out, o, ", earlier versions stay forever.</p>\n" as *u8) 1285 let vd: *u8 = sys_mmap(512) 1286 of_vdir(root, name, vc, vd) 1287 let sp: *u8 = sys_mmap(600) 1288 var so: i64 = of_cat(sp, 0, vd); so = of_cat(sp, so, "/spec.txt" as *u8); sp[so] = 0 as u8 1289 let spec: *u8 = sys_mmap(131072) 1290 let sl: i64 = of_read_file(sp, spec, 131071) 1291 if sl > 0 { spec[sl] = 0 as u8 } 1292 // ---- the visual editor card: toolbar + WYSIWYG surface + raw-spec fallback, all in one form ---- 1293 o = of_cat(out, o, "<div class='card'>" as *u8) 1294 o = of_toolbar(out, o, ks) 1295 o = of_cat(out, o, "<form id='savef' method='post' action='" as *u8) 1296 o = of_cat(out, o, base) 1297 o = of_cat(out, o, "/save'><input type='hidden' name='kind' value='" as *u8) 1298 o = of_cat(out, o, kb) 1299 o = of_cat(out, o, "'><input type='hidden' name='name' value='" as *u8) 1300 o = of_cat(out, o, name) 1301 o = of_cat(out, o, "'>" as *u8) 1302 if ks == 0 { o = of_render_doc(out, o, spec, sl) } 1303 if ks == 1 { o = of_render_sheet(out, o, spec, sl) } 1304 if ks == 2 { o = of_render_deck(out, o, spec, sl) } 1305 o = of_cat(out, o, "<details id='rawd' class='rawwrap' open><summary>Edit raw spec</summary><textarea id='specta' name='spec'>" as *u8) 1306 if sl > 0 { o = of_esc(out, o, spec) } 1307 o = of_cat(out, o, "</textarea></details>" as *u8) 1308 o = of_cat(out, o, "<p><button type='submit' class='save'>Save as v" as *u8) 1309 o = of_catn(out, o, vc + 1) 1310 o = of_cat(out, o, "</button> <span id='savest' class='hint'></span></p></form></div>\n" as *u8) 1311 if ks == 0 { 1312 o = of_cat(out, o, "<div class='card'><h2 style='margin:0 0 8px'>&#10024; AI draft</h2><p class='hint' style='margin:0 0 10px'>Continue this document with our OWN sovereign no-float 0.5B model, on our own hardware &mdash; zero OpenAI. The draft is appended as a NEW version you can edit, diff or restore. <b>Experimental</b> (0.5B is small &mdash; quality is rough).</p><form method='post' action='" as *u8) 1313 o = of_cat(out, o, base) 1314 o = of_cat(out, o, "/ai'><input type='hidden' name='name' value='" as *u8) 1315 o = of_cat(out, o, name) 1316 o = of_cat(out, o, "'><button type='submit' class='ghost'>&#10024; Continue with AI</button></form></div>\n" as *u8) 1317 // ---- OF-A4 AGENT MODE card: plan -> execute -> refine, one turn per click ---- 1318 let agb: *u8 = sys_mmap(8192) 1319 let agn: i64 = of_agent_read(root, name, agb, 8000) 1320 let agsteps: i64 = of_agent_tagcount(agb, agn, 83) 1321 let agdone: i64 = of_agent_done(agb, agn) 1322 o = of_cat(out, o, "<div class='card'><h2 style='margin:0 0 8px'>&#129302; Agent mode</h2><p class='hint' style='margin:0 0 10px'>Give it a goal and it <b>plans, executes and refines</b> multi-step work on this document &mdash; on OUR own sovereign model, zero cloud. Each step lands as a <b>new version</b>, so the agent's whole trajectory stays diffable and restorable. One click = one turn (each model turn takes several seconds). <b>Experimental</b> (0.5B is small &mdash; quality is rough).</p>" as *u8) 1323 if agn <= 0 { 1324 o = of_cat(out, o, "<form method='post' action='" as *u8) 1325 o = of_cat(out, o, base) 1326 o = of_cat(out, o, "/agent'><input type='hidden' name='name' value='" as *u8) 1327 o = of_cat(out, o, name) 1328 o = of_cat(out, o, "'><input type='text' name='goal' placeholder='e.g. add a section on summer plans and a closing' style='width:100%;margin-bottom:8px'><button type='submit' class='ghost'>&#129302; Plan it</button></form>" as *u8) 1329 } else { 1330 let agg: *u8 = sys_mmap(512) 1331 of_agent_tagline(agb, agn, 71, 0, agg, 512) 1332 o = of_cat(out, o, "<p style='margin:0 0 6px'><b>Goal:</b> " as *u8) 1333 o = of_esc(out, o, agg) 1334 o = of_cat(out, o, "</p><ol class='agentplan' style='margin:0 0 10px 18px'>" as *u8) 1335 let agsb: *u8 = sys_mmap(256) 1336 var agi: i64 = 0 1337 while agi < agsteps { 1338 of_agent_tagline(agb, agn, 83, agi, agsb, 256) 1339 o = of_cat(out, o, "<li>" as *u8) 1340 if agi < agdone { o = of_cat(out, o, "<span class='cnt'>&#10003;</span> " as *u8) } 1341 o = of_esc(out, o, agsb) 1342 o = of_cat(out, o, "</li>" as *u8) 1343 agi = agi + 1 1344 } 1345 o = of_cat(out, o, "</ol>" as *u8) 1346 if agdone > agsteps { 1347 o = of_cat(out, o, "<p class='hint' style='margin:0 0 10px'>&#10003; <b>Run complete</b> &mdash; every step drafted and refined. Give a new goal to start another run.</p><form method='post' action='" as *u8) 1348 o = of_cat(out, o, base) 1349 o = of_cat(out, o, "/agent'><input type='hidden' name='name' value='" as *u8) 1350 o = of_cat(out, o, name) 1351 o = of_cat(out, o, "'><input type='text' name='goal' placeholder='new goal' style='width:100%;margin-bottom:8px'><button type='submit' class='ghost'>&#129302; Plan again</button></form>" as *u8) 1352 } else { 1353 o = of_cat(out, o, "<p class='hint' style='margin:0 0 10px'>Turn " as *u8) 1354 o = of_catn(out, o, agdone + 1) 1355 o = of_cat(out, o, " of " as *u8) 1356 o = of_catn(out, o, agsteps + 1) 1357 o = of_cat(out, o, " (the last turn is the refine pass).</p><form method='post' action='" as *u8) 1358 o = of_cat(out, o, base) 1359 o = of_cat(out, o, "/agent'><input type='hidden' name='name' value='" as *u8) 1360 o = of_cat(out, o, name) 1361 o = of_cat(out, o, "'><button type='submit' class='ghost'>&#9654; " as *u8) 1362 if agdone < agsteps { o = of_cat(out, o, "Run next step" as *u8) } else { o = of_cat(out, o, "Run final refine" as *u8) } 1363 o = of_cat(out, o, "</button></form>" as *u8) 1364 } 1365 } 1366 o = of_cat(out, o, "</div>\n" as *u8) 1367 } 1368 // OF-A2 (sheets) / OF-A3 (decks): the same sovereign seat, in each artifact's own grammar. Agent mode stays 1369 // doc-only for now (it appends prose), so these get the single-turn card, not the plan/execute/refine loop. 1370 if ks != 0 { 1371 o = of_cat(out, o, "<div class='card'><h2 style='margin:0 0 8px'>&#10024; " as *u8) 1372 if ks == 1 { o = of_cat(out, o, "AI insight" as *u8) } else { o = of_cat(out, o, "AI slide" as *u8) } 1373 o = of_cat(out, o, "</h2><p class='hint' style='margin:0 0 10px'>" as *u8) 1374 if ks == 1 { 1375 o = of_cat(out, o, "Ask our OWN sovereign model to read this grid and add one insight as a new row &mdash; zero cloud. It lands as a NEW version you can edit, diff or restore." as *u8) 1376 } else { 1377 o = of_cat(out, o, "Ask our OWN sovereign model to draft the next slide (title + bullet) from the deck so far &mdash; zero cloud. It lands as a NEW version you can edit, diff or restore." as *u8) 1378 } 1379 o = of_cat(out, o, " <b>Experimental</b> (0.5B is small &mdash; quality is rough).</p><form method='post' action='" as *u8) 1380 o = of_cat(out, o, base) 1381 o = of_cat(out, o, "/ai'><input type='hidden' name='name' value='" as *u8) 1382 o = of_cat(out, o, name) 1383 o = of_cat(out, o, "'><button type='submit' class='ghost'>&#10024; " as *u8) 1384 if ks == 1 { o = of_cat(out, o, "Add an AI insight" as *u8) } else { o = of_cat(out, o, "Draft the next slide" as *u8) } 1385 o = of_cat(out, o, "</button></form></div>\n" as *u8) 1386 } 1387 // U3: star + folder controls. Zero-JS forms, same additive spirit as everything else -- neither touches 1388 // the spec or the version chain, so organising a file can never damage its content. 1389 o = of_cat(out, o, "<div class='card'><h2 style='margin:0 0 8px'>Organise</h2><p class='hint' style='margin:0 0 10px'>Star it to pin it to the top of your library, or file it in a folder. Neither changes the document or its history.</p><form method='post' action='" as *u8) 1390 o = of_cat(out, o, base) 1391 o = of_cat(out, o, "/star' style='display:inline'><input type='hidden' name='name' value='" as *u8) 1392 o = of_cat(out, o, name) 1393 o = of_cat(out, o, "'><button type='submit' class='ghost'>" as *u8) 1394 if of_starred(root, name) == 1 { o = of_cat(out, o, "&starf; Starred (click to unstar)" as *u8) } else { o = of_cat(out, o, "&star; Star this file" as *u8) } 1395 o = of_cat(out, o, "</button></form> <form method='post' action='" as *u8) 1396 o = of_cat(out, o, base) 1397 o = of_cat(out, o, "/folder' style='display:inline'><input type='hidden' name='name' value='" as *u8) 1398 o = of_cat(out, o, name) 1399 o = of_cat(out, o, "'><input type='text' name='folder' placeholder='folder' value='" as *u8) 1400 let fcur: *u8 = sys_mmap(64) 1401 of_folder_get(root, name, fcur, 64) 1402 o = of_esc(out, o, fcur) 1403 o = of_cat(out, o, "'><button type='submit' class='ghost'>File it</button></form></div> 1404" as *u8) 1405 o = of_cat(out, o, "<div class='card'><h2 style='margin:0 0 8px'>Versions</h2><div class='tablewrap'><table><thead><tr><th>Version</th><th>Changes</th><th>Preview</th><th>Download</th><th>Restore</th></tr></thead><tbody>" as *u8) 1406 var v: i64 = vc 1407 while v >= 1 { 1408 o = of_cat(out, o, "<tr><td><b>v" as *u8); o = of_catn(out, o, v) 1409 o = of_cat(out, o, "</b>" as *u8) 1410 if of_manifest_is_auto(root, name, v) == 1 { o = of_cat(out, o, " <span class='cnt'>(auto)</span>" as *u8) } 1411 o = of_cat(out, o, "</td><td>" as *u8) 1412 if v >= 2 { o = of_cat(out, o, "<a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/diff/" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "/" as *u8); o = of_catn(out, o, v - 1); o = of_cat(out, o, "/" as *u8); o = of_catn(out, o, v); o = of_cat(out, o, "'>vs v" as *u8); o = of_catn(out, o, v - 1); o = of_cat(out, o, "</a>" as *u8) } 1413 else { o = of_cat(out, o, "&mdash;" as *u8) } 1414 o = of_cat(out, o, "</td><td><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/preview/" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "/v" as *u8); o = of_catn(out, o, v) 1415 o = of_cat(out, o, "'>preview</a></td><td><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/file/" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "/v" as *u8); o = of_catn(out, o, v) 1416 o = of_cat(out, o, "'>" as *u8); o = of_esc(out, o, name); o = of_cat(out, o, "." as *u8); o = of_cat(out, o, of_ext(ks)); o = of_cat(out, o, "</a></td><td>" as *u8) 1417 if v < vc { 1418 o = of_cat(out, o, "<form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/restore' style='margin:0'><input type='hidden' name='name' value='" as *u8); o = of_cat(out, o, name); o = of_cat(out, o, "'><input type='hidden' name='version' value='" as *u8); o = of_catn(out, o, v); o = of_cat(out, o, "'><button type='submit' class='ghost' title='Save this version as the newest (nothing is lost)'>Restore</button></form>" as *u8) 1419 } else { o = of_cat(out, o, "<span class='cnt'>current</span>" as *u8) } 1420 o = of_cat(out, o, "</td></tr>" as *u8) 1421 v = v - 1 1422 } 1423 o = of_cat(out, o, "</tbody></table></div></div></main>\n<script src='" as *u8) 1424 o = of_cat(out, o, base) 1425 o = of_cat(out, o, "/app.js'></script>\n" as *u8) 1426 o = of_page_foot(out, o) 1427 return o 1428} 1429func of_err(out: *u8, status: *u8, msg: *u8) -> i64 { 1430 // body = "LOUD: " + msg + "\n" (6 + len(msg) + 1). Emit Content-Length + Connection: close so a STRICT 1431 // CL-stop client (our own nx_https TLS stack / nx_office_live_verify) can frame the error without hanging. 1432 let bl: i64 = 6 + of_slen(msg) + 1 1433 var o: i64 = of_cat(out, 0, "HTTP/1.1 " as *u8) 1434 o = of_cat(out, o, status) 1435 o = of_cat(out, o, "\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: " as *u8) 1436 o = of_catn(out, o, bl) 1437 o = of_cat(out, o, "\r\nConnection: close\r\n\r\nLOUD: " as *u8) 1438 o = of_cat(out, o, msg) 1439 o = of_catc(out, o, 10) 1440 return o 1441} 1442// ---- IN-APP AI (the marquee): draft with OUR OWN sovereign no-float 0.5B LLM served on loopback :11434 ---- 1443// HONEST: it is a 0.5B integer model -- draft quality is EXPERIMENTAL, not GPT/Copilot-class. The point is 1444// sovereign convergence: our office drafting off our own model on our own hardware, zero OpenAI. Graceful: 1445// if the seat is down, the caller returns a LOUD 503 (never an office error). 1446// extract the FIRST "content":"..." value (JSON-unescaped) from buf into out; returns length or -1 if absent. 1447func of_extract_json_content(buf: *u8, n: i64, out: *u8, cap: i64) -> i64 { 1448 let key: *u8 = "\"content\":\"" as *u8 1449 var i: i64 = 0 1450 var found: i64 = 0 - 1 1451 while i + 11 <= n { 1452 var m: i64 = 1; var j: i64 = 0 1453 while j < 11 { if buf[i+j] != key[j] { m = 0; j = 11 } else { j = j + 1 } } 1454 if m == 1 { found = i + 11; i = n } else { i = i + 1 } 1455 } 1456 if found < 0 { return 0 - 1 } 1457 var o: i64 = 0 1458 var k: i64 = found 1459 var go: i64 = 1 1460 while go == 1 { 1461 if k >= n { go = 0 } else { 1462 let c: i64 = buf[k] as i64 1463 if c == 34 { go = 0 } else { 1464 if c == 92 { 1465 k = k + 1 1466 if k < n { 1467 let e: i64 = buf[k] as i64 1468 if e == 110 { if o < cap-1 { out[o] = 10 as u8; o = o + 1 } } else { 1469 if e == 116 { if o < cap-1 { out[o] = 9 as u8; o = o + 1 } } else { 1470 if e == 34 { if o < cap-1 { out[o] = 34 as u8; o = o + 1 } } else { 1471 if e == 92 { if o < cap-1 { out[o] = 92 as u8; o = o + 1 } } else { 1472 if o < cap-1 { out[o] = buf[k]; o = o + 1 } } } } } 1473 } 1474 k = k + 1 1475 } else { 1476 if o < cap-1 { out[o] = buf[k]; o = o + 1 } 1477 k = k + 1 1478 } } 1479 } 1480 } 1481 out[o] = 0 as u8 1482 return o 1483} 1484// flatten a doc spec (H/B/I/P lines; T table rows) into plain running text for the LLM prompt (strip the 2-char 1485// kind prefix, '|' table cells -> spaces). Keeps only the tail (last ~cap chars) since the 0.5B context is small. 1486func of_spec_to_text(spec: *u8, sl: i64, out: *u8, cap: i64) -> i64 { 1487 let tmp: *u8 = sys_mmap(sl + 16) 1488 var to: i64 = 0 1489 var i: i64 = 0 1490 while i < sl { 1491 var e: i64 = i 1492 while e < sl { if spec[e] == (10 as u8) { break } e = e + 1 } 1493 var cs: i64 = i 1494 if e >= i + 2 { if spec[i+1] == (32 as u8) { cs = i + 2 } } 1495 var k: i64 = cs 1496 while k < e { let c: i64 = spec[k] as i64; if c == 124 { tmp[to] = 32 as u8 } else { tmp[to] = spec[k] } to = to + 1; k = k + 1 } 1497 tmp[to] = 32 as u8; to = to + 1 1498 i = e + 1 1499 } 1500 // keep the tail 1501 var start: i64 = 0 1502 if to > cap { start = to - cap } 1503 var oo: i64 = 0 1504 var t: i64 = start 1505 while t < to { if oo < cap - 1 { out[oo] = tmp[t]; oo = oo + 1 } t = t + 1 } 1506 out[oo] = 0 as u8 1507 return oo 1508} 1509// call our sovereign LLM seat (POST /complete on 127.0.0.1:11434) to continue `prompt`. Fills out with the 1510// completion (JSON-unescaped); returns length, or <0 on conn-fail / non-200 / empty (caller -> graceful 503). 1511// max_tokens is a PARAMETER (of_llm_complete keeps the proven 24 default) because agent mode needs a longer 1512// budget for its PLAN turn than for a step turn. Keep every value small enough that ONE call finishes well 1513// under the edge's ~15s proxy timeout -- measured ~0.29s/token, so 32 tokens ~= 9.3s is the safe ceiling. 1514func of_llm_complete_n(prompt: *u8, plen: i64, out: *u8, cap: i64, maxtok: i64) -> i64 { 1515 let bcap: i64 = plen * 2 + 256 1516 let jb: *u8 = sys_mmap(bcap) 1517 var bo: i64 = of_cat(jb, 0, "{\"content\":\"" as *u8) 1518 var i: i64 = 0 1519 while i < plen { 1520 let c: i64 = prompt[i] as i64 1521 if c == 34 { jb[bo] = 92 as u8; bo = bo + 1; jb[bo] = 34 as u8; bo = bo + 1 } else { 1522 if c == 92 { jb[bo] = 92 as u8; bo = bo + 1; jb[bo] = 92 as u8; bo = bo + 1 } else { 1523 if c == 10 { jb[bo] = 92 as u8; bo = bo + 1; jb[bo] = 110 as u8; bo = bo + 1 } else { 1524 if c == 9 { jb[bo] = 32 as u8; bo = bo + 1 } else { 1525 if c == 13 { } else { 1526 if c < 32 { jb[bo] = 32 as u8; bo = bo + 1 } else { jb[bo] = prompt[i]; bo = bo + 1 } } } } } } 1527 i = i + 1 1528 } 1529 bo = of_cat(jb, bo, "\",\"max_tokens\":" as *u8) 1530 bo = of_catn(jb, bo, maxtok) 1531 bo = of_catc(jb, bo, 125) 1532 jb[bo] = 0 as u8 1533 let addr: *u8 = sys_mmap(16) 1534 nx_http_client_sockaddr_ipv4(addr, 127, 0, 0, 1, 11434) 1535 let fd: i64 = sys_socket(2, 1, 0) 1536 if fd < 0 { return 0 - 1 } 1537 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 - 2 } 1538 sys_set_socket_timeout(fd, 14) 1539 let ct: *u8 = "application/json" as *u8 1540 let req: *u8 = sys_mmap(8192 + bo) 1541 let reqlen: i64 = nx_http_client_build_request_post("/complete" as *u8, 9, "localhost" as *u8, 9, ct, 16, jb, bo, req) 1542 sys_write(fd, req, reqlen) 1543 let resp: *u8 = sys_mmap(262144) 1544 var rn: i64 = 0 1545 var go: i64 = 1 1546 while go == 1 { if rn >= 262143 { go = 0 } else { let r: i64 = sys_read(fd, (resp as i64 + rn) as *u8, 262143 - rn); if r <= 0 { go = 0 } else { rn = rn + r } } } 1547 sys_close(fd) 1548 if rn < 15 { return 0 - 3 } 1549 if (resp[9] as i64) != 50 { return 0 - 4 } 1550 if (resp[10] as i64) != 48 { return 0 - 4 } 1551 if (resp[11] as i64) != 48 { return 0 - 4 } 1552 resp[rn] = 0 as u8 1553 let cl: i64 = of_extract_json_content(resp, rn, out, cap) 1554 if cl <= 0 { return 0 - 5 } 1555 return cl 1556} 1557func of_llm_complete(prompt: *u8, plen: i64, out: *u8, cap: i64) -> i64 { 1558 return of_llm_complete_n(prompt, plen, out, cap, 24) 1559} 1560// OF-A2 / OF-A3: each kind gets its OWN prompt builder rather than one function with a kind flag, so the 1561// census can needle a DEFINITION per axis (its own anti-faking law: a comment mentioning a bare name must 1562// never flip an axis) and each stays independently editable. 1563func of_ai_prompt_sheet(ptext: *u8, out: *u8) -> i64 { 1564 var o: i64 = of_cat(out, 0, "This is a spreadsheet, tab separated. Data: " as *u8) 1565 o = of_cat(out, o, ptext) 1566 o = of_cat(out, o, " . State one short insight about this data:" as *u8) 1567 out[o] = 0 as u8 1568 return o 1569} 1570func of_ai_prompt_deck(ptext: *u8, out: *u8) -> i64 { 1571 var o: i64 = of_cat(out, 0, "Slides so far: " as *u8) 1572 o = of_cat(out, o, ptext) 1573 o = of_cat(out, o, " . Write the next slide as a title, then one bullet:" as *u8) 1574 out[o] = 0 as u8 1575 return o 1576} 1577 1578// ================= OF-A4 AGENT MODE -- multi-step PLAN -> EXECUTE -> REFINE ========================= 1579// The 2025/2026 headline differentiator the SOTA bar names (M365 Copilot "Agent Mode" GA 2026-04-22: 1580// Copilot plans, executes and refines MULTI-STEP work in the app). Ours runs on OUR OWN sovereign seat. 1581// 1582// WHY IT IS STEP-DRIVEN, NOT A SINGLE LONG CALL: one LLM turn costs ~7-10s and the edge proxy times out at 1583// ~15s. A 4-step agent in one request would ALWAYS exceed it. So each POST /office/agent advances EXACTLY 1584// ONE turn and persists its state; the loop is driven by repeated POSTs. That is a real constraint honestly 1585// handled, not a reduced capability -- the agent is still multi-step, state-carrying and resumable. 1586// 1587// WHERE THE EXCEEDS IS: every executed step lands as a NEW ADDITIVE VERSION through of_save, so the entire 1588// agent trajectory is diffable and restorable. The cloud leaders' agent steps are ephemeral -- you get the 1589// final artifact and the reasoning is gone. Ours is content-addressed history you can walk backwards. 1590// 1591// State lives in a SIDECAR <root>/<name>/agent.txt so the document spec stays PURE CONTENT (no new spec line 1592// kinds = no renderer risk, no format migration). Line-based, same idiom as the spec: 1593// G <goal> one line, the operator's goal 1594// S <step> one line per planned step, in order 1595// D <n> how many turns are DONE. n in 0..steps = executing; n > steps = refined + complete. 1596func of_agent_path(root: *u8, name: *u8, out: *u8) -> i64 { 1597 var o: i64 = of_cat(out, 0, root); o = of_cat(out, o, "/" as *u8); o = of_cat(out, o, name) 1598 o = of_cat(out, o, "/agent.txt" as *u8); out[o] = 0 as u8 1599 return o 1600} 1601func of_agent_read(root: *u8, name: *u8, buf: *u8, cap: i64) -> i64 { 1602 let p: *u8 = sys_mmap(512) 1603 of_agent_path(root, name, p) 1604 return of_read_file(p, buf, cap) 1605} 1606func of_agent_write(root: *u8, name: *u8, buf: *u8, n: i64) -> i64 { 1607 let p: *u8 = sys_mmap(512) 1608 of_agent_path(root, name, p) 1609 return of_write_file(p, buf, n) 1610} 1611// count the lines whose FIRST byte is `tag` (tags: 71='G' 83='S' 68='D') 1612func of_agent_tagcount(buf: *u8, n: i64, tag: i64) -> i64 { 1613 var c: i64 = 0 1614 var i: i64 = 0 1615 var bol: i64 = 1 1616 while i < n { 1617 if bol == 1 { if (buf[i] as i64) == tag { c = c + 1 } } 1618 if buf[i] == (10 as u8) { bol = 1 } else { bol = 0 } 1619 i = i + 1 1620 } 1621 return c 1622} 1623// copy the body of the idx-th (0-based) line carrying `tag` into out; returns its length (0 = no such line) 1624func of_agent_tagline(buf: *u8, n: i64, tag: i64, idx: i64, out: *u8, cap: i64) -> i64 { 1625 var seen: i64 = 0 1626 var i: i64 = 0 1627 var bol: i64 = 1 1628 while i < n { 1629 if bol == 1 { 1630 if (buf[i] as i64) == tag { 1631 if seen == idx { 1632 var s: i64 = i + 1 1633 if s < n { if buf[s] == (32 as u8) { s = s + 1 } } 1634 var o: i64 = 0 1635 while s < n { 1636 if buf[s] == (10 as u8) { s = n } else { 1637 if o < cap - 1 { out[o] = buf[s]; o = o + 1 } 1638 s = s + 1 } 1639 } 1640 out[o] = 0 as u8 1641 return o 1642 } 1643 seen = seen + 1 1644 } 1645 } 1646 if buf[i] == (10 as u8) { bol = 1 } else { bol = 0 } 1647 i = i + 1 1648 } 1649 out[0] = 0 as u8 1650 return 0 1651} 1652func of_agent_done(buf: *u8, n: i64) -> i64 { 1653 let t: *u8 = sys_mmap(32) 1654 if of_agent_tagline(buf, n, 68, 0, t, 32) <= 0 { return 0 } 1655 return of_atoi(t) 1656} 1657// Turn ONE free-text LLM completion into at most 4 plan steps. Splits on newlines first; if that yields 1658// fewer than 2 steps, falls back to splitting on ". " so a single-line reply still becomes a real plan. 1659// Leading list punctuation (digits, '.', ')', '-', '*', spaces) is stripped so "1. Draft intro" -> "Draft intro". 1660// Emits "S <step>\n" lines into out. Returns how many steps were written (0 = the model gave us nothing usable). 1661func of_agent_plan_parse(comp: *u8, cl: i64, out: *u8, cap: i64) -> i64 { 1662 var steps: i64 = 0 1663 var o: i64 = 0 1664 var ph: i64 = 0 1665 while ph < 2 { 1666 steps = 0 1667 o = 0 1668 var i: i64 = 0 1669 while i < cl { 1670 if steps >= 4 { i = cl } else { 1671 // find the end of this chunk: newline always ends it; in phase 1 ". " ends it too 1672 var e: i64 = i 1673 var scan: i64 = 1 1674 while scan == 1 { 1675 if e >= cl { scan = 0 } else { 1676 if comp[e] == (10 as u8) { scan = 0 } else { 1677 var brk: i64 = 0 1678 if ph == 1 { 1679 if comp[e] == (46 as u8) { 1680 if e + 1 < cl { if comp[e+1] == (32 as u8) { brk = 1 } } 1681 } 1682 } 1683 if brk == 1 { scan = 0 } else { e = e + 1 } 1684 } 1685 } 1686 } 1687 // strip leading list punctuation + spaces: "1. Draft intro" -> "Draft intro" 1688 var s: i64 = i 1689 var strip: i64 = 1 1690 while strip == 1 { 1691 if s >= e { strip = 0 } else { 1692 let c: i64 = comp[s] as i64 1693 if c == 32 { s = s + 1 } else { 1694 if c == 46 { s = s + 1 } else { 1695 if c == 41 { s = s + 1 } else { 1696 if c == 45 { s = s + 1 } else { 1697 if c == 42 { s = s + 1 } else { 1698 if c >= 48 { if c <= 57 { s = s + 1 } else { strip = 0 } } else { strip = 0 } } } } } } 1699 } 1700 } 1701 // keep it only if real text survives (>= 4 chars) and it fits the buffer 1702 if e - s >= 4 { 1703 if o + (e - s) + 8 < cap { 1704 o = of_cat(out, o, "S " as *u8) 1705 var k: i64 = s 1706 var w: i64 = 0 1707 while k < e { 1708 if w < 120 { 1709 let c2: i64 = comp[k] as i64 1710 if c2 == 9 { out[o] = 32 as u8 } else { if c2 == 13 { out[o] = 32 as u8 } else { out[o] = comp[k] } } 1711 o = o + 1; w = w + 1 1712 } 1713 k = k + 1 1714 } 1715 o = of_catc(out, o, 10) 1716 steps = steps + 1 1717 } 1718 } 1719 i = e + 1 1720 } 1721 } 1722 // phase 0 (newline split) wins if it found a real multi-step plan; else retry splitting sentences 1723 if steps >= 2 { ph = 2 } else { ph = ph + 1 } 1724 } 1725 out[o] = 0 as u8 1726 return steps 1727} 1728// Insert Content-Length before the header terminator so a STRICT HTTP client (the sovereign Nishi Browser) 1729// can frame the body. Static docroot files send it; our HTML pages (of_page_top) did NOT, so the in-browser 1730// GET of /office failed to frame after a clean TLS handshake (Waterfox tolerates it by reading until close). 1731// out holds a full "HTTP/.. \r\n<headers>\r\n\r\n<body>" of length n; returns the new length. Idempotent 1732// (skips if Content-Length already present, e.g. of_serve downloads) and fail-safe (no room -> unchanged). 1733func of_cl_frame(out: *u8, n: i64, cap: i64) -> i64 { 1734 if n <= 0 { return n } 1735 var p: i64 = 0 - 1 1736 var i: i64 = 0 1737 while i + 3 < n { if out[i] == (13 as u8) { if out[i+1] == (10 as u8) { if out[i+2] == (13 as u8) { if out[i+3] == (10 as u8) { p = i; i = n } } } } i = i + 1 } 1738 if p < 0 { return n } 1739 if of_memhas(out, p, "Content-Length:" as *u8) == 1 { return n } 1740 let bodylen: i64 = n - (p + 4) 1741 let ins: *u8 = sys_mmap(48) 1742 var io: i64 = of_cat(ins, 0, "Content-Length: " as *u8) 1743 io = of_catn(ins, io, bodylen) 1744 io = of_catc(ins, io, 13); io = of_catc(ins, io, 10) 1745 if n + io >= cap { return n } 1746 var s: i64 = n - 1 1747 while s >= p + 2 { out[s + io] = out[s]; s = s - 1 } 1748 var k: i64 = 0 1749 while k < io { out[p + 2 + k] = ins[k]; k = k + 1 } 1750 return n + io 1751} 1752// serve <root>/<name>/v<N>/{file.<ext>|preview.html}. mode 0 = artifact download, 1 = preview html. 1753func of_serve(root: *u8, name: *u8, vn: i64, mode: i64, out: *u8, cap: i64) -> i64 { 1754 let kb: *u8 = sys_mmap(64) 1755 let vc: i64 = of_manifest_count(root, name, kb, 64) 1756 if vc <= 0 { return 0 - 1 } 1757 if vn < 1 { return 0 - 1 } 1758 if vn > vc { return 0 - 1 } 1759 let ki: i64 = of_kind_idx(kb) 1760 var ks: i64 = ki 1761 if ks < 0 { ks = 0 } 1762 let vd: *u8 = sys_mmap(512) 1763 of_vdir(root, name, vn, vd) 1764 let fp: *u8 = sys_mmap(600) 1765 var fo: i64 = of_cat(fp, 0, vd) 1766 if mode == 1 { fo = of_cat(fp, fo, "/preview.html" as *u8) } else { fo = of_cat(fp, fo, "/file." as *u8); fo = of_cat(fp, fo, of_ext(ks)) } 1767 fp[fo] = 0 as u8 1768 let fb: *u8 = sys_mmap(1048576) 1769 let fl: i64 = of_read_file(fp, fb, 1048576) 1770 if fl < 0 { return 0 - 1 } 1771 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: " as *u8) 1772 if mode == 1 { o = of_cat(out, o, "text/html; charset=utf-8" as *u8) } else { o = of_cat(out, o, of_ct(ks)) } 1773 o = of_cat(out, o, "\r\nContent-Length: " as *u8) 1774 o = of_catn(out, o, fl) 1775 if mode == 0 { 1776 o = of_cat(out, o, "\r\nContent-Disposition: attachment; filename=" as *u8) 1777 o = of_cat(out, o, name) 1778 o = of_cat(out, o, "." as *u8) 1779 o = of_cat(out, o, of_ext(ks)) 1780 } 1781 o = of_cat(out, o, "\r\n\r\n" as *u8) 1782 var i: i64 = 0 1783 while i < fl { if o + i < cap { out[o + i] = fb[i] } i = i + 1 } 1784 return o + fl 1785} 1786// parse "/<pfx>/<name>/v<N>" -> name + N. returns N (>=1) or -1. 1787func of_parse_nv(path: *u8, pfxlen: i64, name: *u8, ncap: i64) -> i64 { 1788 var i: i64 = pfxlen 1789 var t: i64 = 0 1790 while path[i] != (0 as u8) { if path[i] == (47 as u8) { break } if t < ncap - 1 { name[t] = path[i]; t = t + 1 } i = i + 1 } 1791 name[t] = 0 as u8 1792 if t == 0 { return 0 - 1 } 1793 if path[i] != (47 as u8) { return 0 - 1 } 1794 i = i + 1 1795 if path[i] != (118 as u8) { return 0 - 1 } 1796 i = i + 1 1797 var v: i64 = 0 1798 var d: i64 = 0 1799 while path[i] != (0 as u8) { 1800 let c: i64 = path[i] as i64 1801 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); d = d + 1; i = i + 1 } else { return 0 - 1 } } else { return 0 - 1 } 1802 } 1803 if d == 0 { return 0 - 1 } 1804 return v 1805} 1806 1807// the app: request bytes -> response bytes. returns response length. 1808// base = the URL mount prefix (e.g. "/office" when reverse-proxied at nishifamily.com/office, "" for the raw 1809// daemon). The edge forwards the FULL path verbatim, so we STRIP base off the incoming path before routing while 1810// build "doc:<name>" / "user:<handle>" refs for the ReBAC plane. 1811func of_docref(out: *u8, name: *u8) -> i64 { var o: i64 = of_cat(out, 0, "doc:" as *u8); o = of_cat(out, o, name); out[o] = 0 as u8; return o } 1812func of_meref(out: *u8, handle: *u8) -> i64 { var o: i64 = of_cat(out, 0, "user:" as *u8); o = of_cat(out, o, handle); out[o] = 0 as u8; return o } 1813func of_grpref(out: *u8, gname: *u8) -> i64 { var o: i64 = of_cat(out, 0, "group:" as *u8); o = of_cat(out, o, gname); out[o] = 0 as u8; return o } 1814// the userset "group:<g>#member" -- sharing a doc with THIS grants every member of the group (Zanzibar userset). 1815func of_grpmemberref(out: *u8, gname: *u8) -> i64 { var o: i64 = of_grpref(out, gname); o = of_cat(out, o, "#member" as *u8); out[o] = 0 as u8; return o } 1816// a share target handle must be [a-zA-Z0-9_-] 1..64 -- so ':' or '#' can never forge a type/userset in a tuple ref. 1817func of_tok_ok(s: *u8) -> i64 { 1818 if s[0] == (0 as u8) { return 0 } 1819 var i: i64 = 0 1820 while s[i] != (0 as u8) { 1821 let c: i64 = s[i] as i64 1822 var ok: i64 = 0 1823 if c >= 97 { if c <= 122 { ok = 1 } } 1824 if c >= 65 { if c <= 90 { ok = 1 } } 1825 if c >= 48 { if c <= 57 { ok = 1 } } 1826 if c == 95 { ok = 1 } 1827 if c == 45 { ok = 1 } 1828 if ok == 0 { return 0 } 1829 if i >= 64 { return 0 } 1830 i = i + 1 1831 } 1832 return 1 1833} 1834func of_sess_field(dst: *u8, off: i64, tok: *u8) -> i64 { 1835 var o: i64 = off 1836 if tok[0] != (0 as u8) { o = of_cat(dst, o, "<input type='hidden' name='sess' value='" as *u8); o = of_cat(dst, o, tok); o = of_cat(dst, o, "'>" as *u8) } 1837 return o 1838} 1839// authz refusal (fail-closed: owned doc, caller not allowed). 1840func of_deny_read(out: *u8) -> i64 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 1841func of_deny_write(out: *u8) -> i64 { return of_err(out, "403 Forbidden" as *u8, "this document is owned; you need edit access (ask the owner to share it)" as *u8) } 1842func of_after_colon(s: *u8) -> *u8 { var i: i64 = 0; while s[i] != (0 as u8) { if (s[i] as i64) == 58 { return (s as i64 + i + 1) as *u8 } i = i + 1 } return s } 1843// read/write authorization for a doc by name. UNOWNED = PUBLIC (1). OWNED = the caller needs the relation. 1844func of_read_ok(azprefix: *u8, me: *u8, authed: i64, nm: *u8) -> i64 { 1845 let dref: *u8 = sys_mmap(320); of_docref(dref, nm) 1846 if rb_obj_has_owner(azprefix, dref) == 0 { return 1 } 1847 if authed == 0 { return 0 } 1848 return rb_check2(azprefix, OF_RELATE, me, "read" as *u8, dref, 0) 1849} 1850func of_write_ok(azprefix: *u8, me: *u8, authed: i64, nm: *u8) -> i64 { 1851 let dref: *u8 = sys_mmap(320); of_docref(dref, nm) 1852 if rb_obj_has_owner(azprefix, dref) == 0 { return 1 } 1853 if authed == 0 { return 0 } 1854 return rb_check2(azprefix, OF_RELATE, me, "write" as *u8, dref, 0) 1855} 1856// SHARE(grant=1)/UNSHARE(grant=0) an OWNED doc -- owner-only. doc:<name> viewer|editor user:<who>. 1857func of_do_share(base: *u8, azprefix: *u8, me: *u8, handle: *u8, authed: i64, body: *u8, blen: i64, out: *u8, grant: i64) -> i64 { 1858 if authed == 0 { return of_deny_write(out) } 1859 let nm: *u8 = sys_mmap(64); let who: *u8 = sys_mmap(64); let lv: *u8 = sys_mmap(16) 1860 of_form_get(body, blen, "name" as *u8, nm, 64) 1861 of_form_get(body, blen, "who" as *u8, who, 64) 1862 of_form_get(body, blen, "level" as *u8, lv, 16) 1863 if of_name_ok(nm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 1864 if of_tok_ok(who) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad handle" as *u8) } 1865 let dref: *u8 = sys_mmap(320); of_docref(dref, nm) 1866 if rb_obj_has_owner(azprefix, dref) == 0 { return of_err(out, "400 Bad Request" as *u8, "save the document first (it becomes yours), then share it" as *u8) } 1867 if rb_check(azprefix, me, "manage" as *u8, dref, 0) == 0 { return of_deny_write(out) } 1868 var rel: *u8 = "viewer" as *u8 1869 if of_seq(lv, "editor" as *u8) == 1 { rel = "editor" as *u8 } 1870 // target=group -> share with the userset group:<who>#member (every group member gets access); else a person. 1871 let tgt: *u8 = sys_mmap(16); of_form_get(body, blen, "target" as *u8, tgt, 16) 1872 let wref: *u8 = sys_mmap(320) 1873 if of_seq(tgt, "group" as *u8) == 1 { of_grpmemberref(wref, who) } else { of_meref(wref, who) } 1874 rb_put(azprefix, dref, rel, wref, handle, grant) 1875 var o: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 1876 o = of_cat(out, o, base); o = of_cat(out, o, "/manage/" as *u8); o = of_cat(out, o, nm); o = of_cat(out, o, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 1877 return o 1878} 1879// copy s into dst up to a '#' (or end), NUL-term. "elders#member" -> "elders". 1880func of_before_hash(dst: *u8, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { if (s[i] as i64) == 35 { dst[i] = 0 as u8; return i } dst[i] = s[i]; i = i + 1 } dst[i] = 0 as u8; return i } 1881// render one shared-with row + its unshare button. subref = "user:<h>" (a person) OR "group:<g>#member" (a group). 1882func of_share_row(out: *u8, off: i64, base: *u8, nm: *u8, subref: *u8, rel: *u8, tok: *u8) -> i64 { 1883 var target: *u8 = "user" as *u8 1884 let who: *u8 = sys_mmap(96) 1885 let label: *u8 = sys_mmap(128) 1886 if of_starts(subref, "group:" as *u8) == 1 { 1887 target = "group" as *u8 1888 of_before_hash(who, of_after_colon(subref)) 1889 var lo: i64 = of_cat(label, 0, "group " as *u8); lo = of_cat(label, lo, who); label[lo] = 0 as u8 1890 } else { 1891 var wo: i64 = of_cat(who, 0, of_after_colon(subref)); who[wo] = 0 as u8 1892 var lo: i64 = of_cat(label, 0, who); label[lo] = 0 as u8 1893 } 1894 var o: i64 = of_cat(out, off, "<div class='row'>" as *u8) 1895 o = of_cat(out, o, label); o = of_cat(out, o, " <span class='mut'>" as *u8); o = of_cat(out, o, rel) 1896 o = of_cat(out, o, "</span> <form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/unshare' style='display:inline'>" as *u8) 1897 o = of_sess_field(out, o, tok) 1898 o = of_cat(out, o, "<input type='hidden' name='name' value='" as *u8); o = of_cat(out, o, nm) 1899 o = of_cat(out, o, "'><input type='hidden' name='who' value='" as *u8); o = of_cat(out, o, who) 1900 o = of_cat(out, o, "'><input type='hidden' name='target' value='" as *u8); o = of_cat(out, o, target) 1901 o = of_cat(out, o, "'><input type='hidden' name='level' value='" as *u8); o = of_cat(out, o, rel) 1902 o = of_cat(out, o, "'><button class='ghost'>unshare</button></form></div>" as *u8) 1903 return o 1904} 1905// the owner's Sharing panel for a doc. returns inner-HTML length, or negative (=-1 unauth / -2 not-owned / -3 not-owner). 1906func of_managepage(base: *u8, azprefix: *u8, me: *u8, authed: i64, nm: *u8, tok: *u8, out: *u8, cap: i64) -> i64 { 1907 let dref: *u8 = sys_mmap(320); of_docref(dref, nm) 1908 if authed == 0 { return 0 - 1 } 1909 if rb_obj_has_owner(azprefix, dref) == 0 { return 0 - 2 } 1910 if rb_check(azprefix, me, "manage" as *u8, dref, 0) == 0 { return 0 - 3 } 1911 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Sharing</title><style>body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;max-width:720px;margin:5vh auto;padding:0 20px;color:#222}.row{padding:8px 0;border-bottom:1px solid #e5e7eb}.mut{color:#888}button{border:0;border-radius:8px;padding:8px 12px;background:rgb(41,84,164);color:#fff;font-weight:600}.ghost{background:#eee;color:#333;padding:4px 10px}input,select{padding:7px;border:1px solid #ccc;border-radius:8px;margin-right:6px}</style></head><body><div class='wrap'><h2>Sharing: " as *u8); o = of_cat(out, o, nm); o = of_cat(out, o, "</h2>" as *u8) 1912 o = of_cat(out, o, "<p class='mut'>You own this document. Share it with a person by handle as viewer or editor; unshare revokes.</p><h3>Shared with</h3>" as *u8) 1913 let vv: *i64 = sys_mmap(8 * 64) as *i64 1914 let nv: i64 = rb_list_subjects(azprefix, dref, "viewer" as *u8, vv, 64) 1915 let ee: *i64 = sys_mmap(8 * 64) as *i64 1916 let ne: i64 = rb_list_subjects(azprefix, dref, "editor" as *u8, ee, 64) 1917 if nv == 0 { if ne == 0 { o = of_cat(out, o, "<div class='mut'>(not shared with anyone yet)</div>" as *u8) } } 1918 var qi: i64 = 0 1919 while qi < ne { o = of_share_row(out, o, base, nm, ee[qi] as *u8, "editor" as *u8, tok); qi = qi + 1 } 1920 qi = 0 1921 while qi < nv { o = of_share_row(out, o, base, nm, vv[qi] as *u8, "viewer" as *u8, tok); qi = qi + 1 } 1922 o = of_cat(out, o, "<h3>Share with someone</h3><form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/share'>" as *u8) 1923 o = of_sess_field(out, o, tok) 1924 o = of_cat(out, o, "<input type='hidden' name='name' value='" as *u8); o = of_cat(out, o, nm) 1925 o = of_cat(out, o, "'><input name='who' placeholder='person handle or group name'> <select name='target'><option value='user'>person</option><option value='group'>group</option></select> <select name='level'><option>viewer</option><option>editor</option></select> <button>Share</button></form>" as *u8) 1926 o = of_cat(out, o, "<p class='mut'>Share with a <a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/groups'>group</a> and every member gets access.</p>" as *u8) 1927 o = of_cat(out, o, "<p><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/doc/" as *u8); o = of_cat(out, o, nm); o = of_cat(out, o, "'>&larr; back to document</a></p></div></body></html>" as *u8) 1928 return o 1929} 1930 1931// GROUP membership handler: add(1)/remove(0) a member. The caller bootstraps/owns the group. 303 -> /groups. 1932func of_do_group(base: *u8, azprefix: *u8, me: *u8, handle: *u8, authed: i64, body: *u8, blen: i64, out: *u8, add: i64) -> i64 { 1933 if authed == 0 { return of_deny_write(out) } 1934 let gn: *u8 = sys_mmap(64); let who: *u8 = sys_mmap(64) 1935 of_form_get(body, blen, "group" as *u8, gn, 64) 1936 of_form_get(body, blen, "who" as *u8, who, 64) 1937 if of_tok_ok(gn) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad group name" as *u8) } 1938 if of_tok_ok(who) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad handle" as *u8) } 1939 let gref: *u8 = sys_mmap(320); of_grpref(gref, gn) 1940 if rb_may_grant(azprefix, me, gref) == 0 { return of_deny_write(out) } 1941 if rb_obj_has_owner(azprefix, gref) == 0 { 1942 rb_put(azprefix, gref, "owner" as *u8, me, handle, 1) 1943 rb_put(azprefix, gref, "member" as *u8, me, handle, 1) 1944 } 1945 let wref: *u8 = sys_mmap(320); of_meref(wref, who) 1946 rb_put(azprefix, gref, "member" as *u8, wref, handle, add) 1947 var o: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 1948 o = of_cat(out, o, base); o = of_cat(out, o, "/groups\r\n\r\n" as *u8) 1949 return o 1950} 1951// the caller's GROUPS page (owned groups + members + management forms). returns a FULL HTTP response. 1952func of_groupspage(base: *u8, azprefix: *u8, me: *u8, authed: i64, tok: *u8, out: *u8, cap: i64) -> i64 { 1953 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>My groups</title><style>body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;max-width:720px;margin:5vh auto;padding:0 20px;color:#222}.row{padding:6px 0;border-bottom:1px solid #eee}.mut{color:#888}button{border:0;border-radius:8px;padding:8px 12px;background:rgb(41,84,164);color:#fff;font-weight:600}.ghost{background:#eee;color:#333;padding:3px 9px}input{padding:7px;border:1px solid #ccc;border-radius:8px;margin-right:6px}</style></head><body><h2>My groups</h2>" as *u8) 1954 if authed == 0 { o = of_cat(out, o, "<p>Sign in to manage groups.</p></body></html>" as *u8); return o } 1955 o = of_cat(out, o, "<p class='mut'>Groups you own. Add people by handle; share a document with a group and every member gets access (revoke a member and they lose it everywhere).</p>" as *u8) 1956 let gg: *i64 = sys_mmap(8 * 64) as *i64 1957 let ng: i64 = rb_list_objects_for_sub(azprefix, me, "owner" as *u8, gg, 64) 1958 if ng == 0 { o = of_cat(out, o, "<div class='mut'>(no groups yet -- create one below)</div>" as *u8) } 1959 var gi: i64 = 0 1960 while gi < ng { 1961 let gref: *u8 = gg[gi] as *u8 1962 let gbare: *u8 = of_after_colon(gref) 1963 o = of_cat(out, o, "<h3>" as *u8); o = of_cat(out, o, gbare); o = of_cat(out, o, "</h3>" as *u8) 1964 let mm: *i64 = sys_mmap(8 * 64) as *i64 1965 let nm2: i64 = rb_list_subjects(azprefix, gref, "member" as *u8, mm, 64) 1966 var mi: i64 = 0 1967 while mi < nm2 { 1968 let mbare: *u8 = of_after_colon(mm[mi] as *u8) 1969 o = of_cat(out, o, "<div class='row'>" as *u8); o = of_cat(out, o, mbare) 1970 o = of_cat(out, o, " <form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/group-remove' style='display:inline'>" as *u8) 1971 o = of_sess_field(out, o, tok) 1972 o = of_cat(out, o, "<input type='hidden' name='group' value='" as *u8); o = of_cat(out, o, gbare) 1973 o = of_cat(out, o, "'><input type='hidden' name='who' value='" as *u8); o = of_cat(out, o, mbare) 1974 o = of_cat(out, o, "'><button class='ghost'>remove</button></form></div>" as *u8) 1975 mi = mi + 1 1976 } 1977 o = of_cat(out, o, "<form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/group-add'>" as *u8) 1978 o = of_sess_field(out, o, tok) 1979 o = of_cat(out, o, "<input type='hidden' name='group' value='" as *u8); o = of_cat(out, o, gbare) 1980 o = of_cat(out, o, "'><input name='who' placeholder='handle to add'><button>Add member</button></form>" as *u8) 1981 gi = gi + 1 1982 } 1983 o = of_cat(out, o, "<h3>Create a group</h3><form method='post' action='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/group-add'>" as *u8) 1984 o = of_sess_field(out, o, tok) 1985 o = of_cat(out, o, "<input name='group' placeholder='new group name'><input name='who' placeholder='first member handle'><button>Create</button></form>" as *u8) 1986 o = of_cat(out, o, "<p><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/'>&larr; back to office</a></p></body></html>" as *u8) 1987 return o 1988} 1989 1990// REVERSE INDEX: append the doc: objects where (obj, rel, sub) is current into out[] (deduped). returns new count. 1991func of_collect_docs(azprefix: *u8, sub: *u8, rel: *u8, out: *i64, n: i64, cap: i64) -> i64 { 1992 let tmp: *i64 = sys_mmap(8 * 64) as *i64 1993 let k: i64 = rb_list_objects_for_sub(azprefix, sub, rel, tmp, 64) 1994 var i: i64 = 0; var nn: i64 = n 1995 while i < k { 1996 let o: *u8 = tmp[i] as *u8 1997 if of_starts(o, "doc:" as *u8) == 1 { if nn < cap { if rb_in_list(out, nn, o) == 0 { out[nn] = o as i64; nn = nn + 1 } } } 1998 i = i + 1 1999 } 2000 return nn 2001} 2002// "what documents can I access?" -- the reverse-index query: docs I own/view/edit DIRECTLY, plus docs shared with 2003// any GROUP I'm a member of (userset expansion on the read side). Deduped. This is the SOTA list-objects primitive. 2004func of_my_docs(azprefix: *u8, me: *u8, out: *i64, cap: i64) -> i64 { 2005 var n: i64 = 0 2006 n = of_collect_docs(azprefix, me, "owner" as *u8, out, n, cap) 2007 n = of_collect_docs(azprefix, me, "viewer" as *u8, out, n, cap) 2008 n = of_collect_docs(azprefix, me, "editor" as *u8, out, n, cap) 2009 let grps: *i64 = sys_mmap(8 * 64) as *i64 2010 let ng: i64 = rb_list_objects_for_sub(azprefix, me, "member" as *u8, grps, 64) 2011 var gi: i64 = 0 2012 while gi < ng { 2013 let g: *u8 = grps[gi] as *u8 2014 if of_starts(g, "group:" as *u8) == 1 { 2015 let gm: *u8 = sys_mmap(320); var go: i64 = of_cat(gm, 0, g); go = of_cat(gm, go, "#member" as *u8); gm[go] = 0 as u8 2016 n = of_collect_docs(azprefix, gm, "viewer" as *u8, out, n, cap) 2017 n = of_collect_docs(azprefix, gm, "editor" as *u8, out, n, cap) 2018 } 2019 gi = gi + 1 2020 } 2021 return n 2022} 2023// the "Shared with me" page: every doc the caller can reach (direct or via a group). FULL HTTP response. 2024func of_sharedpage(base: *u8, azprefix: *u8, me: *u8, authed: i64, out: *u8, cap: i64) -> i64 { 2025 var o: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Shared with me</title><style>body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;max-width:720px;margin:5vh auto;padding:0 20px;color:#222}.row{padding:8px 0;border-bottom:1px solid #eee}.mut{color:#888}a{color:rgb(41,84,164)}</style></head><body><h2>Documents you can access</h2>" as *u8) 2026 if authed == 0 { o = of_cat(out, o, "<p>Sign in to see documents shared with you.</p></body></html>" as *u8); return o } 2027 let docs: *i64 = sys_mmap(8 * 128) as *i64 2028 let nd: i64 = of_my_docs(azprefix, me, docs, 128) 2029 o = of_cat(out, o, "<p class='mut'>Owned by you, shared with you directly, or shared with a group you belong to.</p>" as *u8) 2030 if nd == 0 { o = of_cat(out, o, "<div class='mut'>(nothing yet)</div>" as *u8) } 2031 var di: i64 = 0 2032 while di < nd { 2033 let dbare: *u8 = of_after_colon(docs[di] as *u8) 2034 o = of_cat(out, o, "<div class='row'><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/doc/" as *u8); o = of_cat(out, o, dbare); o = of_cat(out, o, "'>" as *u8); o = of_cat(out, o, dbare); o = of_cat(out, o, "</a></div>" as *u8) 2035 di = di + 1 2036 } 2037 o = of_cat(out, o, "<p><a href='" as *u8); o = of_cat(out, o, base); o = of_cat(out, o, "/'>&larr; back to office</a></p></body></html>" as *u8) 2038 return o 2039} 2040 2041// generated links KEEP base -> the app is mount-point agnostic (works at :8030/ and at nishifamily.com/office/). 2042// OPEN-MODE wrapper: no handle -> no authz gating (current PUBLIC behavior; the 34-test offline gate runs here). 2043func of_handle(root: *u8, base: *u8, req: *u8, reqlen: i64, out: *u8, cap: i64) -> i64 { 2044 let mt: *u8 = sys_mmap(8); mt[0] = 0 as u8 2045 return of_handle_auth(root, base, req, reqlen, out, cap, mt, OF_AUTHZ, mt) 2046} 2047// AUTH-AWARE handler. handle = resolved caller ("" = unauthed/open). azprefix = ReBAC tuple store. tok = session 2048// token embedded in owner forms (zero-JS). A doc with NO owner tuple is PUBLIC; an OWNED doc: read needs 2049// rb_check(user:me, read, doc), a new version needs write, only the owner may share/unshare/manage. 2050func of_handle_auth(root: *u8, base: *u8, req: *u8, reqlen: i64, out: *u8, cap: i64, handle: *u8, azprefix: *u8, tok: *u8) -> i64 { 2051 var authed: i64 = 0; let me: *u8 = sys_mmap(320); me[0] = 0 as u8 2052 if handle[0] != (0 as u8) { authed = 1; of_meref(me, handle) } 2053 // fullpath = token between the first two spaces 2054 let fullpath: *u8 = sys_mmap(300) 2055 var i: i64 = 0 2056 while i < reqlen { if req[i] == (32 as u8) { i = i + 1; break } i = i + 1 } 2057 var t: i64 = 0 2058 while i < reqlen { if req[i] == (32 as u8) { break } if t < 299 { fullpath[t] = req[i]; t = t + 1 } i = i + 1 } 2059 fullpath[t] = 0 as u8 2060 // split off the query string (home search/sort/filter reads it) so routing sees a clean path 2061 let qs: *u8 = sys_mmap(300) 2062 qs[0] = 0 as u8 2063 var qi: i64 = 0 2064 while fullpath[qi] != (0 as u8) { 2065 if fullpath[qi] == (63 as u8) { 2066 var qq: i64 = qi + 1 2067 var qt: i64 = 0 2068 while fullpath[qq] != (0 as u8) { if qt < 299 { qs[qt] = fullpath[qq]; qt = qt + 1 } qq = qq + 1 } 2069 qs[qt] = 0 as u8 2070 fullpath[qi] = 0 as u8 2071 } else { qi = qi + 1 } 2072 } 2073 // strip the mount prefix -> path is relative to the app root ("/office/doc/x" -> "/doc/x", "/office" -> "") 2074 var path: *u8 = fullpath 2075 if base[0] != (0 as u8) { 2076 var bm: i64 = 1 2077 var bi: i64 = 0 2078 while base[bi] != (0 as u8) { if fullpath[bi] != base[bi] { bm = 0; bi = of_slen(base) } else { bi = bi + 1 } } 2079 if bm == 1 { path = (fullpath as i64 + of_slen(base)) as *u8 } 2080 } 2081 var is_post: i64 = 0 2082 if req[0] == (80 as u8) { if req[1] == (79 as u8) { is_post = 1 } } 2083 if is_post == 1 { 2084 var is_save: i64 = of_seq(path, "/save" as *u8) 2085 var is_restore: i64 = of_seq(path, "/restore" as *u8) 2086 var is_share: i64 = of_seq(path, "/share" as *u8) 2087 var is_unshare: i64 = of_seq(path, "/unshare" as *u8) 2088 var is_gadd: i64 = of_seq(path, "/group-add" as *u8) 2089 var is_gremove: i64 = of_seq(path, "/group-remove" as *u8) 2090 var is_ai: i64 = of_seq(path, "/ai" as *u8) 2091 var is_agent: i64 = of_seq(path, "/agent" as *u8) 2092 var is_star: i64 = of_seq(path, "/star" as *u8) 2093 var is_folder: i64 = of_seq(path, "/folder" as *u8) 2094 if (is_save + is_restore + is_share + is_unshare + is_gadd + is_gremove + is_ai + is_agent + is_star + is_folder) == 0 { return of_err(out, "404 Not Found" as *u8, "no such POST route" as *u8) } 2095 // same-origin guard: if Origin present it must contain our Host 2096 let ob: *u8 = sys_mmap(300) 2097 let hb: *u8 = sys_mmap(300) 2098 of_hdr_get(req, reqlen, "Origin:" as *u8, ob, 300) 2099 of_hdr_get(req, reqlen, "Host:" as *u8, hb, 300) 2100 if ob[0] != (0 as u8) { if hb[0] != (0 as u8) { 2101 if of_memhas(ob, of_slen(ob), hb) == 0 { return of_err(out, "403 Forbidden" as *u8, "cross-origin POST refused" as *u8) } 2102 } } 2103 var bs: i64 = 0 - 1 2104 var j: i64 = 0 2105 while j + 3 < reqlen { 2106 if req[j] == (13 as u8) { if req[j+1] == (10 as u8) { if req[j+2] == (13 as u8) { if req[j+3] == (10 as u8) { bs = j + 4; j = reqlen } } } } 2107 j = j + 1 2108 } 2109 if bs < 0 { return of_err(out, "400 Bad Request" as *u8, "no body" as *u8) } 2110 let body: *u8 = (req as i64 + bs) as *u8 2111 let blen: i64 = reqlen - bs 2112 // SHARE / UNSHARE an owned doc (owner-only; additive grant / tombstone revoke). GROUP add/remove members. 2113 if is_share == 1 { return of_do_share(base, azprefix, me, handle, authed, body, blen, out, 1) } 2114 if is_unshare == 1 { return of_do_share(base, azprefix, me, handle, authed, body, blen, out, 0) } 2115 if is_gadd == 1 { return of_do_group(base, azprefix, me, handle, authed, body, blen, out, 1) } 2116 if is_gremove == 1 { return of_do_group(base, azprefix, me, handle, authed, body, blen, out, 0) } 2117 // AI CONTINUE: draft a continuation with OUR OWN sovereign 0.5B model (:11434) + append it as a NEW 2118 // version (additive -- the AI draft is a version you can diff/restore). Doc kind only. Graceful 503 if 2119 // the seat is down. Write-gated like a save (owned docs need edit access). 2120 if is_ai == 1 { 2121 let anm: *u8 = sys_mmap(64) 2122 of_form_get(body, blen, "name" as *u8, anm, 64) 2123 if of_name_ok(anm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 2124 if of_write_ok(azprefix, me, authed, anm) == 0 { return of_deny_write(out) } 2125 let akb: *u8 = sys_mmap(64) 2126 let avc: i64 = of_manifest_count(root, anm, akb, 64) 2127 if avc <= 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2128 // ONE sovereign seat, THREE kinds. The prompt AND the append are kind-specific so the model edits each 2129 // artifact in ITS OWN grammar: prose for a doc, an insight ROW for a sheet (OF-A2, the "Fill with 2130 // Gemini" class), a whole new SLIDE for a deck (OF-A3, prompt-to-slide). Every kind stays additive. 2131 let akind: i64 = of_kind_idx(akb) 2132 let aspec: *u8 = sys_mmap(131073) 2133 let asl: i64 = of_read_version_spec(root, anm, avc, aspec, 131072) 2134 let ptext: *u8 = sys_mmap(2048) 2135 let ptl: i64 = of_spec_to_text(aspec, asl, ptext, 1200) 2136 let aprompt: *u8 = sys_mmap(4096) 2137 var api: i64 = 0 2138 if akind == 1 { api = of_ai_prompt_sheet(ptext, aprompt) } else { 2139 if akind == 2 { api = of_ai_prompt_deck(ptext, aprompt) } else { api = of_cat(aprompt, 0, ptext) } 2140 } 2141 aprompt[api] = 0 as u8 2142 let comp: *u8 = sys_mmap(8192) 2143 let cl: i64 = of_llm_complete(aprompt, api, comp, 8000) 2144 if cl <= 0 { return of_err(out, "503 Service Unavailable" as *u8, "the sovereign AI seat (our own 0.5B model on :11434) is not answering -- start nx_f32_llm_serve or try again" as *u8) } 2145 let nspec: *u8 = sys_mmap(140000) 2146 var no: i64 = 0 2147 var ci: i64 = 0 2148 while ci < asl { nspec[no] = aspec[ci]; no = no + 1; ci = ci + 1 } 2149 if no > 0 { if nspec[no-1] != (10 as u8) { nspec[no] = 10 as u8; no = no + 1 } } 2150 if akind == 2 { 2151 // deck: everything up to the first sentence end becomes the TITLE, the remainder the BULLET 2152 var dsp: i64 = 0 - 1 2153 var dfi: i64 = 0 2154 while dfi < cl { if comp[dfi] == (46 as u8) { dsp = dfi; dfi = cl } else { dfi = dfi + 1 } } 2155 var tend: i64 = cl 2156 if dsp > 3 { tend = dsp } 2157 no = of_cat(nspec, no, "S " as *u8) 2158 var dti: i64 = 0 2159 while dti < tend { let tc: i64 = comp[dti] as i64; if tc == 10 { nspec[no] = 32 as u8 } else { if tc == 9 { nspec[no] = 32 as u8 } else { nspec[no] = comp[dti] } } no = no + 1; dti = dti + 1 } 2160 if dsp > 3 { if dsp + 2 < cl { 2161 no = of_catc(nspec, no, 10) 2162 no = of_cat(nspec, no, "B " as *u8) 2163 var dbi: i64 = dsp + 1 2164 while dbi < cl { let bc: i64 = comp[dbi] as i64; if bc == 10 { nspec[no] = 32 as u8 } else { if bc == 9 { nspec[no] = 32 as u8 } else { nspec[no] = comp[dbi] } } no = no + 1; dbi = dbi + 1 } 2165 } } 2166 } else { 2167 // sheet -> a REAL two-cell TSV row so the insight lands IN the grid; doc -> a P paragraph 2168 if akind == 1 { no = of_cat(nspec, no, "AI insight" as *u8); no = of_catc(nspec, no, 9) } else { no = of_cat(nspec, no, "P " as *u8) } 2169 var di: i64 = 0 2170 while di < cl { let c: i64 = comp[di] as i64; if c == 10 { nspec[no] = 32 as u8 } else { if c == 9 { nspec[no] = 32 as u8 } else { nspec[no] = comp[di] } } no = no + 1; di = di + 1 } 2171 } 2172 nspec[no] = 0 as u8 2173 let nvn: i64 = of_save(root, base, anm, akb, nspec, no, 0) 2174 if nvn > 0 { 2175 var ao: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 2176 ao = of_cat(out, ao, base); ao = of_cat(out, ao, "/doc/" as *u8); ao = of_cat(out, ao, anm); ao = of_cat(out, ao, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 2177 return ao 2178 } 2179 return of_err(out, "500 Internal Server Error" as *u8, "AI draft save failed" as *u8) 2180 } 2181 // AGENT MODE (OF-A4, the 2026 marquee): ONE POST = ONE agent turn. PLAN when there is no plan yet (or 2182 // a fresh goal arrives), otherwise EXECUTE the next step, and once every step is drafted run one final 2183 // REFINE turn. Each executed turn lands as a NEW VERSION, so the whole trajectory is diffable/restorable. 2184 // D is bumped ONLY AFTER its version lands, so a 503 mid-run is safely retryable (no step is skipped). 2185 if is_agent == 1 { 2186 let gnm: *u8 = sys_mmap(64) 2187 of_form_get(body, blen, "name" as *u8, gnm, 64) 2188 if of_name_ok(gnm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 2189 if of_write_ok(azprefix, me, authed, gnm) == 0 { return of_deny_write(out) } 2190 let gkb: *u8 = sys_mmap(64) 2191 let gvc: i64 = of_manifest_count(root, gnm, gkb, 64) 2192 if gvc <= 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2193 if of_kind_idx(gkb) != 0 { return of_err(out, "400 Bad Request" as *u8, "agent mode is for documents (doc kind) only" as *u8) } 2194 let goal: *u8 = sys_mmap(512) 2195 of_form_get(body, blen, "goal" as *u8, goal, 512) 2196 let ast: *u8 = sys_mmap(8192) 2197 let an: i64 = of_agent_read(root, gnm, ast, 8000) 2198 // ground EVERY turn in the CURRENT document, not the one the plan was written against 2199 let gspec: *u8 = sys_mmap(131073) 2200 let gsl: i64 = of_read_version_spec(root, gnm, gvc, gspec, 131072) 2201 let gtext: *u8 = sys_mmap(2048) 2202 let gtl: i64 = of_spec_to_text(gspec, gsl, gtext, 1100) 2203 let agloc: *u8 = sys_mmap(512) 2204 2205 // ---- PLAN turn ---- 2206 var want_plan: i64 = 0 2207 if an <= 0 { want_plan = 1 } 2208 if of_slen(goal) > 0 { want_plan = 1 } 2209 if want_plan == 1 { 2210 if of_slen(goal) == 0 { return of_err(out, "400 Bad Request" as *u8, "agent mode needs a goal to plan against" as *u8) } 2211 let pp: *u8 = sys_mmap(4096) 2212 var po2: i64 = of_cat(pp, 0, "You are planning edits to a document. Goal: " as *u8) 2213 po2 = of_cat(pp, po2, goal) 2214 po2 = of_cat(pp, po2, ". Document so far: " as *u8) 2215 po2 = of_cat(pp, po2, gtext) 2216 po2 = of_cat(pp, po2, " . List the steps, one per line:" as *u8) 2217 pp[po2] = 0 as u8 2218 let pcomp: *u8 = sys_mmap(8192) 2219 let pcl: i64 = of_llm_complete_n(pp, po2, pcomp, 8000, 32) 2220 if pcl <= 0 { return of_err(out, "503 Service Unavailable" as *u8, "the sovereign AI seat (our own 0.5B model on :11434) is not answering -- start nx_f32_llm_serve or try again" as *u8) } 2221 let plan: *u8 = sys_mmap(4096) 2222 let nst: i64 = of_agent_plan_parse(pcomp, pcl, plan, 4000) 2223 if nst <= 0 { return of_err(out, "502 Bad Gateway" as *u8, "the model returned no usable plan -- try a more specific goal" as *u8) } 2224 let nstate: *u8 = sys_mmap(8192) 2225 var so2: i64 = of_cat(nstate, 0, "G " as *u8) 2226 so2 = of_cat(nstate, so2, goal) 2227 so2 = of_catc(nstate, so2, 10) 2228 so2 = of_cat(nstate, so2, plan) 2229 so2 = of_cat(nstate, so2, "D 0\n" as *u8) 2230 nstate[so2] = 0 as u8 2231 if of_agent_write(root, gnm, nstate, so2) != 0 { return of_err(out, "500 Internal Server Error" as *u8, "agent plan save failed" as *u8) } 2232 var pl: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 2233 pl = of_cat(out, pl, base); pl = of_cat(out, pl, "/doc/" as *u8); pl = of_cat(out, pl, gnm) 2234 pl = of_cat(out, pl, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 2235 return pl 2236 } 2237 2238 // ---- EXECUTE / REFINE turn ---- 2239 let nsteps: i64 = of_agent_tagcount(ast, an, 83) 2240 let dn: i64 = of_agent_done(ast, an) 2241 if dn > nsteps { return of_err(out, "409 Conflict" as *u8, "this agent run is already complete -- give it a new goal to start another" as *u8) } 2242 let gg: *u8 = sys_mmap(512) 2243 of_agent_tagline(ast, an, 71, 0, gg, 512) 2244 let sp: *u8 = sys_mmap(4096) 2245 var qo: i64 = 0 2246 if dn < nsteps { 2247 let stx: *u8 = sys_mmap(256) 2248 of_agent_tagline(ast, an, 83, dn, stx, 256) 2249 qo = of_cat(sp, 0, "Goal: " as *u8) 2250 qo = of_cat(sp, qo, gg) 2251 qo = of_cat(sp, qo, ". Now do step " as *u8) 2252 qo = of_catn(sp, qo, dn + 1) 2253 qo = of_cat(sp, qo, ": " as *u8) 2254 qo = of_cat(sp, qo, stx) 2255 qo = of_cat(sp, qo, ". Document so far: " as *u8) 2256 qo = of_cat(sp, qo, gtext) 2257 qo = of_cat(sp, qo, " . Write that section:" as *u8) 2258 } else { 2259 qo = of_cat(sp, 0, "Goal: " as *u8) 2260 qo = of_cat(sp, qo, gg) 2261 qo = of_cat(sp, qo, ". Every step is drafted. Document: " as *u8) 2262 qo = of_cat(sp, qo, gtext) 2263 qo = of_cat(sp, qo, " . Write a short closing paragraph that ties it together:" as *u8) 2264 } 2265 sp[qo] = 0 as u8 2266 let scomp: *u8 = sys_mmap(8192) 2267 let scl: i64 = of_llm_complete(sp, qo, scomp, 8000) 2268 if scl <= 0 { return of_err(out, "503 Service Unavailable" as *u8, "the sovereign AI seat (our own 0.5B model on :11434) is not answering -- the agent kept its place, retry this step" as *u8) } 2269 let gnspec: *u8 = sys_mmap(140000) 2270 var gno: i64 = 0 2271 var gci: i64 = 0 2272 while gci < gsl { gnspec[gno] = gspec[gci]; gno = gno + 1; gci = gci + 1 } 2273 if gno > 0 { if gnspec[gno-1] != (10 as u8) { gnspec[gno] = 10 as u8; gno = gno + 1 } } 2274 gno = of_cat(gnspec, gno, "P " as *u8) 2275 var gdi: i64 = 0 2276 while gdi < scl { 2277 let gc: i64 = scomp[gdi] as i64 2278 if gc == 10 { gnspec[gno] = 32 as u8 } else { if gc == 9 { gnspec[gno] = 32 as u8 } else { gnspec[gno] = scomp[gdi] } } 2279 gno = gno + 1; gdi = gdi + 1 2280 } 2281 gnspec[gno] = 0 as u8 2282 let gvn: i64 = of_save(root, base, gnm, gkb, gnspec, gno, 0) 2283 if gvn <= 0 { return of_err(out, "500 Internal Server Error" as *u8, "agent step save failed" as *u8) } 2284 // the version landed -- NOW advance the cursor 2285 let ns2: *u8 = sys_mmap(8192) 2286 let sbuf: *u8 = sys_mmap(256) 2287 var xo: i64 = of_cat(ns2, 0, "G " as *u8) 2288 xo = of_cat(ns2, xo, gg) 2289 xo = of_catc(ns2, xo, 10) 2290 var si: i64 = 0 2291 while si < nsteps { 2292 of_agent_tagline(ast, an, 83, si, sbuf, 256) 2293 xo = of_cat(ns2, xo, "S " as *u8) 2294 xo = of_cat(ns2, xo, sbuf) 2295 xo = of_catc(ns2, xo, 10) 2296 si = si + 1 2297 } 2298 xo = of_cat(ns2, xo, "D " as *u8) 2299 xo = of_catn(ns2, xo, dn + 1) 2300 xo = of_catc(ns2, xo, 10) 2301 ns2[xo] = 0 as u8 2302 of_agent_write(root, gnm, ns2, xo) 2303 var al: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 2304 al = of_cat(out, al, base); al = of_cat(out, al, "/doc/" as *u8); al = of_cat(out, al, gnm) 2305 al = of_cat(out, al, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 2306 return al 2307 } 2308 // U3 STAR / FOLDER: library organisation, write-gated exactly like a save so a reader cannot reorganise 2309 // someone else's library. Neither touches the document spec or its versions. 2310 if is_star == 1 { 2311 let snm: *u8 = sys_mmap(64) 2312 of_form_get(body, blen, "name" as *u8, snm, 64) 2313 if of_name_ok(snm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 2314 if of_write_ok(azprefix, me, authed, snm) == 0 { return of_deny_write(out) } 2315 let skb: *u8 = sys_mmap(64) 2316 if of_manifest_count(root, snm, skb, 64) <= 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2317 of_star_toggle(root, snm) 2318 var so: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other 2319 2320Location: " as *u8) 2321 so = of_cat(out, so, base); so = of_cat(out, so, "/" as *u8) 2322 so = of_cat(out, so, " 2323 2324Content-Length: 0 2325 2326Connection: close 2327 2328 2329 2330" as *u8) 2331 return so 2332 } 2333 if is_folder == 1 { 2334 let fnm: *u8 = sys_mmap(64) 2335 let fvl: *u8 = sys_mmap(64) 2336 of_form_get(body, blen, "name" as *u8, fnm, 64) 2337 of_form_get(body, blen, "folder" as *u8, fvl, 48) 2338 if of_name_ok(fnm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 2339 if of_write_ok(azprefix, me, authed, fnm) == 0 { return of_deny_write(out) } 2340 let fkb: *u8 = sys_mmap(64) 2341 if of_manifest_count(root, fnm, fkb, 64) <= 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2342 // a folder is a plain label; reuse the handle validator so it cannot carry separators or markup 2343 if fvl[0] != (0 as u8) { if of_tok_ok(fvl) == 0 { return of_err(out, "400 Bad Request" as *u8, "folder name must be simple (a-z 0-9 dash underscore)" as *u8) } } 2344 of_folder_set(root, fnm, fvl) 2345 var fo: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other 2346 2347Location: " as *u8) 2348 fo = of_cat(out, fo, base); fo = of_cat(out, fo, "/doc/" as *u8); fo = of_cat(out, fo, fnm) 2349 fo = of_cat(out, fo, " 2350 2351Content-Length: 0 2352 2353Connection: close 2354 2355 2356 2357" as *u8) 2358 return fo 2359 } 2360 // RESTORE: put an old version back by SAVING its spec as a NEW version (additive -- never destroys history). 2361 if is_restore == 1 { 2362 let rnm: *u8 = sys_mmap(64) 2363 let rvs: *u8 = sys_mmap(16) 2364 of_form_get(body, blen, "name" as *u8, rnm, 64) 2365 of_form_get(body, blen, "version" as *u8, rvs, 16) 2366 if of_name_ok(rnm) == 0 { return of_err(out, "400 Bad Request" as *u8, "bad name" as *u8) } 2367 if of_write_ok(azprefix, me, authed, rnm) == 0 { return of_deny_write(out) } 2368 let rkb: *u8 = sys_mmap(64) 2369 let rvc: i64 = of_manifest_count(root, rnm, rkb, 64) 2370 if rvc <= 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2371 let rv: i64 = of_atoi(rvs) 2372 if rv < 1 { return of_err(out, "400 Bad Request" as *u8, "bad version" as *u8) } 2373 if rv > rvc { return of_err(out, "400 Bad Request" as *u8, "bad version" as *u8) } 2374 let rspec: *u8 = sys_mmap(131073) 2375 let rsl: i64 = of_read_version_spec(root, rnm, rv, rspec, 131072) 2376 let rnew: i64 = of_save(root, base, rnm, rkb, rspec, rsl, 0) 2377 if rnew > 0 { 2378 var ro2: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 2379 ro2 = of_cat(out, ro2, base); ro2 = of_cat(out, ro2, "/doc/" as *u8); ro2 = of_cat(out, ro2, rnm); ro2 = of_cat(out, ro2, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 2380 return ro2 2381 } 2382 return of_err(out, "500 Internal Server Error" as *u8, "restore failed (organ rejected the spec)" as *u8) 2383 } 2384 let nm: *u8 = sys_mmap(64) 2385 let kd: *u8 = sys_mmap(32) 2386 let spec: *u8 = sys_mmap(131073) 2387 of_form_get(body, blen, "name" as *u8, nm, 64) 2388 of_form_get(body, blen, "kind" as *u8, kd, 32) 2389 let sl: i64 = of_form_get(body, blen, "spec" as *u8, spec, 131073) 2390 // AUTHZ: an OWNED doc's new version needs write access; remember owned-ness to CLAIM on first save. 2391 var was_owned: i64 = 0 2392 if of_name_ok(nm) == 1 { let dref0: *u8 = sys_mmap(320); of_docref(dref0, nm); if rb_obj_has_owner(azprefix, dref0) == 1 { was_owned = 1 } } 2393 if was_owned == 1 { if of_write_ok(azprefix, me, authed, nm) == 0 { return of_deny_write(out) } } 2394 // ajax=1 = background AUTOSAVE (fetch from office_app.js): answer 200 OK v<N> instead of the 303 2395 let ax: *u8 = sys_mmap(8) 2396 of_form_get(body, blen, "ajax" as *u8, ax, 8) 2397 var auto: i64 = 0 2398 if ax[0] == (49 as u8) { auto = 1 } 2399 let vn: i64 = of_save(root, base, nm, kd, spec, sl, auto) 2400 if vn > 0 { 2401 // CLAIM ownership on first save by an authed user: unowned doc -> owner tuple (now private + shareable). 2402 if was_owned == 0 { if authed == 1 { let dref1: *u8 = sys_mmap(320); of_docref(dref1, nm); rb_put(azprefix, dref1, "owner" as *u8, me, handle, 1) } } 2403 if auto == 1 { 2404 // body = "OK v" + vn + "\n" (4 + digits(vn) + 1) -> Content-Length so strict clients frame it. 2405 var nd: i64 = 1 2406 var tv: i64 = vn 2407 while tv >= 10 { nd = nd + 1; tv = tv / 10 } 2408 var oa: i64 = of_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: " as *u8) 2409 oa = of_catn(out, oa, 4 + nd + 1) 2410 oa = of_cat(out, oa, "\r\nConnection: close\r\n\r\nOK v" as *u8) 2411 oa = of_catn(out, oa, vn) 2412 oa = of_catc(out, oa, 10) 2413 return oa 2414 } 2415 var o: i64 = of_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 2416 o = of_cat(out, o, base) 2417 o = of_cat(out, o, "/doc/" as *u8) 2418 o = of_cat(out, o, nm) 2419 o = of_cat(out, o, "\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" as *u8) 2420 return o 2421 } 2422 if vn == (0 - 2) { return of_err(out, "400 Bad Request" as *u8, "kind mismatch: that name already exists with a different kind" as *u8) } 2423 if vn == (0 - 3) { return of_err(out, "500 Internal Server Error" as *u8, "the format organ rejected the spec (build failed)" as *u8) } 2424 if vn == (0 - 4) { return of_err(out, "500 Internal Server Error" as *u8, "preview render failed" as *u8) } 2425 return of_err(out, "400 Bad Request" as *u8, "invalid name (a-z 0-9 dash, max 40), kind, or empty spec" as *u8) 2426 } 2427 // GET routes 2428 if of_seq(path, "/app.js" as *u8) == 1 { return of_serve_js(out, cap) } 2429 if of_seq(path, "/sw.js" as *u8) == 1 { return of_serve_sw(out, cap) } 2430 if of_seq(path, "/manifest.webmanifest" as *u8) == 1 { return of_serve_manifest(out, base, cap) } 2431 if of_seq(path, "/" as *u8) == 1 { return of_cl_frame(out, of_home(root, base, qs, out, cap, azprefix, me, authed), cap) } 2432 if path[0] == (0 as u8) { return of_cl_frame(out, of_home(root, base, qs, out, cap, azprefix, me, authed), cap) } 2433 if of_starts(path, "/doc/" as *u8) == 1 { 2434 let nm: *u8 = sys_mmap(64) 2435 var q: i64 = 5 2436 var t2: i64 = 0 2437 while path[q] != (0 as u8) { if t2 < 63 { nm[t2] = path[q]; t2 = t2 + 1 } q = q + 1 } 2438 nm[t2] = 0 as u8 2439 if of_name_ok(nm) == 0 { return of_err(out, "404 Not Found" as *u8, "bad name" as *u8) } 2440 if of_read_ok(azprefix, me, authed, nm) == 0 { return of_deny_read(out) } 2441 let n: i64 = of_docpage(root, base, nm, out, cap) 2442 if n < 0 { return of_err(out, "404 Not Found" as *u8, "no such file" as *u8) } 2443 return of_cl_frame(out, n, cap) 2444 } 2445 if of_starts(path, "/preview/" as *u8) == 1 { 2446 let nm: *u8 = sys_mmap(64) 2447 let vn: i64 = of_parse_nv(path, 9, nm, 64) 2448 if vn < 1 { return of_err(out, "404 Not Found" as *u8, "bad preview path" as *u8) } 2449 if of_name_ok(nm) == 0 { return of_err(out, "404 Not Found" as *u8, "bad name" as *u8) } 2450 if of_read_ok(azprefix, me, authed, nm) == 0 { return of_deny_read(out) } 2451 let n: i64 = of_serve(root, nm, vn, 1, out, cap) 2452 if n < 0 { return of_err(out, "404 Not Found" as *u8, "no such version" as *u8) } 2453 return n 2454 } 2455 if of_starts(path, "/file/" as *u8) == 1 { 2456 let nm: *u8 = sys_mmap(64) 2457 let vn: i64 = of_parse_nv(path, 6, nm, 64) 2458 if vn < 1 { return of_err(out, "404 Not Found" as *u8, "bad file path" as *u8) } 2459 if of_name_ok(nm) == 0 { return of_err(out, "404 Not Found" as *u8, "bad name" as *u8) } 2460 if of_read_ok(azprefix, me, authed, nm) == 0 { return of_deny_read(out) } 2461 let n: i64 = of_serve(root, nm, vn, 0, out, cap) 2462 if n < 0 { return of_err(out, "404 Not Found" as *u8, "no such version" as *u8) } 2463 return n 2464 } 2465 if of_starts(path, "/diff/" as *u8) == 1 { 2466 // /diff/<name>/<a>/<b> 2467 let nm: *u8 = sys_mmap(64) 2468 var q: i64 = 6 2469 var t: i64 = 0 2470 while path[q] != (0 as u8) { if path[q] == (47 as u8) { break } if t < 63 { nm[t] = path[q]; t = t + 1 } q = q + 1 } 2471 nm[t] = 0 as u8 2472 if path[q] != (47 as u8) { return of_err(out, "404 Not Found" as *u8, "bad diff path" as *u8) } 2473 q = q + 1 2474 var va: i64 = 0; var da: i64 = 0 2475 while path[q] != (0 as u8) { let c: i64 = path[q] as i64; if c >= 48 { if c <= 57 { va = va * 10 + (c - 48); da = da + 1; q = q + 1 } else { break } } else { break } } 2476 if path[q] != (47 as u8) { return of_err(out, "404 Not Found" as *u8, "bad diff path" as *u8) } 2477 q = q + 1 2478 var vb: i64 = 0; var db: i64 = 0 2479 while path[q] != (0 as u8) { let c: i64 = path[q] as i64; if c >= 48 { if c <= 57 { vb = vb * 10 + (c - 48); db = db + 1; q = q + 1 } else { break } } else { break } } 2480 if da == 0 { return of_err(out, "404 Not Found" as *u8, "bad diff versions" as *u8) } 2481 if db == 0 { return of_err(out, "404 Not Found" as *u8, "bad diff versions" as *u8) } 2482 if of_name_ok(nm) == 0 { return of_err(out, "404 Not Found" as *u8, "bad name" as *u8) } 2483 if of_read_ok(azprefix, me, authed, nm) == 0 { return of_deny_read(out) } 2484 let n: i64 = of_diffpage(root, base, nm, va, vb, out, cap) 2485 if n < 0 { return of_err(out, "404 Not Found" as *u8, "no such file or version" as *u8) } 2486 return of_cl_frame(out, n, cap) 2487 } 2488 if of_seq(path, "/groups" as *u8) == 1 { 2489 let n: i64 = of_groupspage(base, azprefix, me, authed, tok, out, cap) 2490 return of_cl_frame(out, n, cap) 2491 } 2492 if of_seq(path, "/shared-with-me" as *u8) == 1 { 2493 let n: i64 = of_sharedpage(base, azprefix, me, authed, out, cap) 2494 return of_cl_frame(out, n, cap) 2495 } 2496 if of_starts(path, "/manage/" as *u8) == 1 { 2497 let nm: *u8 = sys_mmap(64) 2498 var q: i64 = 8 2499 var t2: i64 = 0 2500 while path[q] != (0 as u8) { if t2 < 63 { nm[t2] = path[q]; t2 = t2 + 1 } q = q + 1 } 2501 nm[t2] = 0 as u8 2502 if of_name_ok(nm) == 0 { return of_err(out, "404 Not Found" as *u8, "bad name" as *u8) } 2503 let n: i64 = of_managepage(base, azprefix, me, authed, nm, tok, out, cap) 2504 if n == (0 - 1) { return of_err(out, "401 Unauthorized" as *u8, "sign in to manage sharing" as *u8) } 2505 if n == (0 - 2) { return of_err(out, "404 Not Found" as *u8, "this document has no owner (it is public)" as *u8) } 2506 if n == (0 - 3) { return of_err(out, "403 Forbidden" as *u8, "only the owner can manage sharing" as *u8) } 2507 return of_cl_frame(out, n, cap) 2508 } 2509 return of_err(out, "404 Not Found" as *u8, "no such route" as *u8) 2510} 2511 2512// ---- the offline gate body (consumed by _hdl_build/nx_office_gate) ---- 2513func of_ck(name: *u8, cond: i64, pass: *i64, fail: *i64) -> i64 { 2514 if cond == 1 { p(" PASS " as *u8); p(name); p("\n" as *u8); pass[0] = pass[0] + 1; return 1 } 2515 p(" FAIL " as *u8); p(name); p("\n" as *u8); fail[0] = fail[0] + 1 2516 return 0 2517} 2518func of_selftest(root: *u8) -> i64 { 2519 p("=== NX-OFFICE SELFTEST root=" as *u8); p(root); p(" (create/edit/version/preview/download, additive-proven, LOUD negatives) ===\n" as *u8) 2520 let base: *u8 = sys_mmap(4) 2521 base[0] = 0 as u8 2522 let pass: *i64 = sys_mmap(16) as *i64 2523 let fail: *i64 = sys_mmap(16) as *i64 2524 pass[0] = 0 2525 fail[0] = 0 2526 let out: *u8 = sys_mmap(1048576) 2527 let req: *u8 = sys_mmap(262144) 2528 let bod: *u8 = sys_mmap(131072) 2529 // T1 empty home 2530 var n: i64 = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2531 of_ck("T1 home renders" as *u8, of_memhas(out, n, "Nishi Office" as *u8), pass, fail) 2532 // helper-free POST builder: body first, then request with exact Content-Length 2533 var bo: i64 = of_cat(bod, 0, "name=family-letter&kind=doc&spec=H+Family+Letter%0AB+Dear+family%0AP+We+love+each+other." as *u8) 2534 var ro: i64 = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2535 ro = of_catn(req, ro, bo) 2536 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2537 ro = of_cat(req, ro, bod) 2538 n = of_handle(root, base, req, ro, out, 1048576) 2539 var t2ok: i64 = 0 2540 if of_memhas(out, n, "303 See Other" as *u8) == 1 { if of_memhas(out, n, "/doc/family-letter" as *u8) == 1 { t2ok = 1 } } 2541 of_ck("T2 create doc v1 (303 redirect)" as *u8, t2ok, pass, fail) 2542 let fb: *u8 = sys_mmap(1048576) 2543 let ap: *u8 = sys_mmap(600) 2544 var ao: i64 = of_cat(ap, 0, root); ao = of_cat(ap, ao, "/family-letter/v1/file.docx" as *u8); ap[ao] = 0 as u8 2545 let fl1: i64 = of_read_file(ap, fb, 1048576) 2546 var t2b: i64 = 0 2547 if fl1 > 200 { t2b = 1 } 2548 of_ck("T2b v1 .docx built on disk" as *u8, t2b, pass, fail) 2549 let pp: *u8 = sys_mmap(600) 2550 var po: i64 = of_cat(pp, 0, root); po = of_cat(pp, po, "/family-letter/v1/preview.html" as *u8); pp[po] = 0 as u8 2551 let pv: *u8 = sys_mmap(1048576) 2552 let pl: i64 = of_read_file(pp, pv, 1048576) 2553 var t2c: i64 = 0 2554 if pl > 0 { t2c = of_memhas(pv, pl, "Family Letter" as *u8) } 2555 of_ck("T2c v1 preview renders the heading" as *u8, t2c, pass, fail) 2556 // T3 doc page round-trips the spec into the editor 2557 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2558 var t3: i64 = 0 2559 if of_memhas(out, n, "H Family Letter" as *u8) == 1 { if of_memhas(out, n, "Save as v2" as *u8) == 1 { t3 = 1 } } 2560 of_ck("T3 editor page carries latest spec (raw fallback)" as *u8, t3, pass, fail) 2561 // T3b VISUAL editor: doc spec rendered as editable blocks (#ed, <h2>, <b>) + app.js wired 2562 var t3b: i64 = 0 2563 if of_memhas(out, n, "id='ed' class='visual doc' contenteditable" as *u8) == 1 { 2564 if of_memhas(out, n, "<h2>Family Letter</h2>" as *u8) == 1 { 2565 if of_memhas(out, n, "<p data-k='B'><b>Dear family</b></p>" as *u8) == 1 { 2566 if of_memhas(out, n, "/app.js'></script>" as *u8) == 1 { t3b = 1 } } } } 2567 of_ck("T3b doc renders editable blocks (#ed h2/b) + app.js" as *u8, t3b, pass, fail) 2568 // T3c the client is served at /app.js as javascript 2569 var na: i64 = of_handle(root, base, "GET /app.js HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 44, out, 1048576) 2570 var t3c: i64 = 0 2571 if of_memhas(out, na, "Content-Type: application/javascript" as *u8) == 1 { if of_memhas(out, na, "getElementById" as *u8) == 1 { t3c = 1 } } 2572 of_ck("T3c /app.js serves the client as javascript" as *u8, t3c, pass, fail) 2573 // snapshot v1 spec bytes for the additive proof 2574 let s1p: *u8 = sys_mmap(600) 2575 var s1o: i64 = of_cat(s1p, 0, root); s1o = of_cat(s1p, s1o, "/family-letter/v1/spec.txt" as *u8); s1p[s1o] = 0 as u8 2576 let s1a: *u8 = sys_mmap(131072) 2577 let s1n: i64 = of_read_file(s1p, s1a, 131072) 2578 // T4 save v2 (edited) 2579 bo = of_cat(bod, 0, "name=family-letter&kind=doc&spec=H+Family+Letter+EDITED%0AP+Now+with+a+second+version." as *u8) 2580 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2581 ro = of_catn(req, ro, bo) 2582 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2583 ro = of_cat(req, ro, bod) 2584 n = of_handle(root, base, req, ro, out, 1048576) 2585 let s2p: *u8 = sys_mmap(600) 2586 var s2o: i64 = of_cat(s2p, 0, root); s2o = of_cat(s2p, s2o, "/family-letter/v2/spec.txt" as *u8); s2p[s2o] = 0 as u8 2587 let s2a: *u8 = sys_mmap(131072) 2588 let s2n: i64 = of_read_file(s2p, s2a, 131072) 2589 var t4: i64 = 0 2590 if of_memhas(out, n, "303" as *u8) == 1 { if s2n > 0 { t4 = 1 } } 2591 of_ck("T4 save again creates v2" as *u8, t4, pass, fail) 2592 let s1b: *u8 = sys_mmap(131072) 2593 let s1n2: i64 = of_read_file(s1p, s1b, 131072) 2594 var addv: i64 = 0 2595 if s1n2 == s1n { if s1n > 0 { 2596 addv = 1 2597 var ci: i64 = 0 2598 while ci < s1n { if s1a[ci] != s1b[ci] { addv = 0; ci = s1n } ci = ci + 1 } 2599 } } 2600 of_ck("T4b ADDITIVE: v1 spec bytes unchanged after v2" as *u8, addv, pass, fail) 2601 // T5 artifact download: response body == on-disk v2 bytes 2602 n = of_handle(root, base, "GET /file/family-letter/v2 HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 60, out, 1048576) 2603 var bs: i64 = 0 - 1 2604 var bi: i64 = 0 2605 while bi + 3 < n { 2606 if out[bi] == (13 as u8) { if out[bi+1] == (10 as u8) { if out[bi+2] == (13 as u8) { if out[bi+3] == (10 as u8) { bs = bi + 4; bi = n } } } } 2607 bi = bi + 1 2608 } 2609 var a2o2: i64 = of_cat(ap, 0, root) 2610 a2o2 = of_cat(ap, a2o2, "/family-letter/v2/file.docx" as *u8) 2611 ap[a2o2] = 0 as u8 2612 let fl2: i64 = of_read_file(ap, fb, 1048576) 2613 var t5: i64 = 0 2614 if bs > 0 { if n - bs == fl2 { if fl2 > 0 { 2615 t5 = 1 2616 var di: i64 = 0 2617 while di < fl2 { if out[bs + di] != fb[di] { t5 = 0; di = fl2 } di = di + 1 } 2618 } } } 2619 var t5h: i64 = of_memhas(out, bs, "wordprocessingml" as *u8) 2620 var t5ok: i64 = 0 2621 if t5 == 1 { if t5h == 1 { t5ok = 1 } } 2622 of_ck("T5 download == on-disk bytes + docx content-type" as *u8, t5ok, pass, fail) 2623 // T6 sheet with a real formula: preview shows the COMPUTED 30 2624 bo = of_cat(bod, 0, "name=family-budget&kind=sheet&spec=Item%09Cost%0AApples%0910%0APears%0920%0ATotal%09=SUM(B2:B3)" as *u8) 2625 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2626 ro = of_catn(req, ro, bo) 2627 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2628 ro = of_cat(req, ro, bod) 2629 n = of_handle(root, base, req, ro, out, 1048576) 2630 var t6r: i64 = of_memhas(out, n, "303" as *u8) 2631 n = of_handle(root, base, "GET /preview/family-budget/v1 HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 63, out, 1048576) 2632 var t6: i64 = 0 2633 if t6r == 1 { if of_memhas(out, n, "30" as *u8) == 1 { t6 = 1 } } 2634 of_ck("T6 sheet formula computed in preview (SUM=30)" as *u8, t6, pass, fail) 2635 // T7 deck 2636 bo = of_cat(bod, 0, "name=reunion-deck&kind=deck&spec=S+Reunion+2026%0AB+Where:+the+lake%0AS+Food%0AB+Friday:+burgers" as *u8) 2637 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2638 ro = of_catn(req, ro, bo) 2639 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2640 ro = of_cat(req, ro, bod) 2641 n = of_handle(root, base, req, ro, out, 1048576) 2642 var t7r: i64 = of_memhas(out, n, "303" as *u8) 2643 n = of_handle(root, base, "GET /preview/reunion-deck/v1 HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 62, out, 1048576) 2644 var t7: i64 = 0 2645 if t7r == 1 { if of_memhas(out, n, "Reunion 2026" as *u8) == 1 { t7 = 1 } } 2646 of_ck("T7 deck builds + preview shows slide title" as *u8, t7, pass, fail) 2647 // T7b VISUAL editors for sheet + deck (editable grid #grid, editable slides #deck) 2648 n = of_handle(root, base, "GET /doc/family-budget HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2649 var t7b: i64 = 0 2650 if of_memhas(out, n, "id='grid' class='visual sheet'" as *u8) == 1 { if of_memhas(out, n, "<td contenteditable='true'>Apples</td>" as *u8) == 1 { t7b = 1 } } 2651 of_ck("T7b sheet renders an editable grid (#grid cells)" as *u8, t7b, pass, fail) 2652 n = of_handle(root, base, "GET /doc/reunion-deck HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 55, out, 1048576) 2653 var t7c: i64 = 0 2654 if of_memhas(out, n, "id='deck' class='visual deck'" as *u8) == 1 { if of_memhas(out, n, "<h3 contenteditable='true'>Reunion 2026</h3>" as *u8) == 1 { if of_memhas(out, n, "data-act='slide'" as *u8) == 1 { t7c = 1 } } } 2655 of_ck("T7c deck renders editable slides (#deck h3 + toolbar)" as *u8, t7c, pass, fail) 2656 // T8 LOUD negatives 2657 bo = of_cat(bod, 0, "name=Bad/Name&kind=doc&spec=P+x" as *u8) 2658 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2659 ro = of_catn(req, ro, bo) 2660 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2661 ro = of_cat(req, ro, bod) 2662 n = of_handle(root, base, req, ro, out, 1048576) 2663 of_ck("T8a bad name refused 400" as *u8, of_memhas(out, n, "400" as *u8), pass, fail) 2664 bo = of_cat(bod, 0, "name=family-letter&kind=sheet&spec=A%09B" as *u8) 2665 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2666 ro = of_catn(req, ro, bo) 2667 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2668 ro = of_cat(req, ro, bod) 2669 n = of_handle(root, base, req, ro, out, 1048576) 2670 of_ck("T8b kind flip on existing name refused 400" as *u8, of_memhas(out, n, "kind mismatch" as *u8), pass, fail) 2671 n = of_handle(root, base, "GET /file/family-letter/v9 HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 60, out, 1048576) 2672 of_ck("T8c missing version 404" as *u8, of_memhas(out, n, "404" as *u8), pass, fail) 2673 bo = of_cat(bod, 0, "name=family-letter&kind=doc&spec=P+evil" as *u8) 2674 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nOrigin: http://evil.example\r\nContent-Length: " as *u8) 2675 ro = of_catn(req, ro, bo) 2676 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2677 ro = of_cat(req, ro, bod) 2678 n = of_handle(root, base, req, ro, out, 1048576) 2679 of_ck("T8d cross-origin POST refused 403" as *u8, of_memhas(out, n, "403" as *u8), pass, fail) 2680 let kb: *u8 = sys_mmap(64) 2681 var t8e: i64 = 0 2682 if of_manifest_count(root, "family-letter" as *u8, kb, 64) == 2 { t8e = 1 } 2683 of_ck("T8e negatives left NO new versions (still 2)" as *u8, t8e, pass, fail) 2684 // T9 home lists all three 2685 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2686 var t9: i64 = 0 2687 if of_memhas(out, n, "family-letter" as *u8) == 1 { if of_memhas(out, n, "family-budget" as *u8) == 1 { if of_memhas(out, n, "reunion-deck" as *u8) == 1 { t9 = 1 } } } 2688 of_ck("T9 home lists all three files" as *u8, t9, pass, fail) 2689 // T10 MOUNTED at /office: the edge forwards the full path; stripping routes it, links carry the prefix. 2690 let mbase: *u8 = "/office" as *u8 2691 n = of_handle(root, mbase, "GET /office HTTP/1.1\r\nHost: nishifamily.com\r\n\r\n" as *u8, 46, out, 1048576) 2692 var t10a: i64 = 0 2693 if of_memhas(out, n, "Nishi Office" as *u8) == 1 { if of_memhas(out, n, "/office/doc/family-letter" as *u8) == 1 { t10a = 1 } } 2694 of_ck("T10a /office home renders + links carry the mount prefix" as *u8, t10a, pass, fail) 2695 n = of_handle(root, mbase, "GET /office/file/family-letter/v2 HTTP/1.1\r\nHost: nishifamily.com\r\n\r\n" as *u8, 68, out, 1048576) 2696 var t10b: i64 = 0 2697 if of_memhas(out, n, "200 OK" as *u8) == 1 { if of_memhas(out, n, "wordprocessingml" as *u8) == 1 { t10b = 1 } } 2698 of_ck("T10b /office/file/.../v2 downloads through the mount" as *u8, t10b, pass, fail) 2699 // T10c a query string (cache-bust, browser params) must not break routing -> home still renders 2700 n = of_handle(root, mbase, "GET /office?v=golive9f2 HTTP/1.1\r\nHost: nishifamily.com\r\n\r\n" as *u8, 58, out, 1048576) 2701 of_ck("T10c /office?query still routes to home (no 404)" as *u8, of_memhas(out, n, "Nishi Office" as *u8), pass, fail) 2702 // T11 VERSION DIFF: controlled v1/v2 with a partial overlap -> LCS keeps 2, adds 1, removes 1. 2703 // NOTE: of_cat does NOT NUL-terminate -> terminate bod at bo so of_cat(req,ro,bod) copies EXACTLY the body 2704 // (else a shorter body trails leftover bytes from a longer prior test = spec contamination). 2705 bo = of_cat(bod, 0, "name=diffdoc&kind=doc&spec=H+Title%0AP+Line+A%0AP+Line+B" as *u8); bod[bo] = 0 as u8 2706 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2707 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2708 n = of_handle(root, base, req, ro, out, 1048576) 2709 bo = of_cat(bod, 0, "name=diffdoc&kind=doc&spec=H+Title%0AP+Line+A+CHANGED%0AP+Line+B" as *u8); bod[bo] = 0 as u8 2710 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2711 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2712 n = of_handle(root, base, req, ro, out, 1048576) 2713 n = of_handle(root, base, "GET /diff/diffdoc/1/2 HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 53, out, 1048576) 2714 var t11: i64 = 0 2715 if of_memhas(out, n, "Changes: v1 &rarr; v2" as *u8) == 1 { 2716 if of_memhas(out, n, "class='ddel'>&minus;&nbsp;P Line A</div>" as *u8) == 1 { 2717 if of_memhas(out, n, "class='dadd'>+&nbsp;P Line A CHANGED</div>" as *u8) == 1 { 2718 if of_memhas(out, n, "class='dkeep'>&nbsp;&nbsp;H Title</div>" as *u8) == 1 { 2719 if of_memhas(out, n, "<b>1</b> line(s) added, <b>1</b> removed" as *u8) == 1 { t11 = 1 } } } } } 2720 of_ck("T11 version diff: LCS keep/add/del + counts (1 added, 1 removed)" as *u8, t11, pass, fail) 2721 // T11b the doc page links the diff for v>=2 2722 n = of_handle(root, base, "GET /doc/diffdoc HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 50, out, 1048576) 2723 of_ck("T11b doc page links 'vs v1' diff for v2" as *u8, of_memhas(out, n, "/diff/diffdoc/1/2'>vs v1" as *u8), pass, fail) 2724 // T11c doc page offers a Restore button for the older version (v1), not for the current (v2) 2725 of_ck("T11c versions table has a Restore form for v1" as *u8, of_memhas(out, n, "/restore' style='margin:0'><input type='hidden' name='name' value='diffdoc'><input type='hidden' name='version' value='1'" as *u8), pass, fail) 2726 // T11d RESTORE: put diffdoc v1 back -> creates v3 == v1 spec, ADDITIVE (v1 + v2 untouched) 2727 bo = of_cat(bod, 0, "name=diffdoc&version=1" as *u8); bod[bo] = 0 as u8 2728 ro = of_cat(req, 0, "POST /restore HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2729 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2730 n = of_handle(root, base, req, ro, out, 1048576) 2731 var t11d: i64 = of_memhas(out, n, "303" as *u8) 2732 // v3 spec must byte-equal v1 spec; manifest now has 3 versions 2733 let rkbx: *u8 = sys_mmap(64) 2734 let rvcx: i64 = of_manifest_count(root, "diffdoc" as *u8, rkbx, 64) 2735 let s1x: *u8 = sys_mmap(4096) 2736 let s3x: *u8 = sys_mmap(4096) 2737 let n1x: i64 = of_read_version_spec(root, "diffdoc" as *u8, 1, s1x, 4095) 2738 let n3x: i64 = of_read_version_spec(root, "diffdoc" as *u8, 3, s3x, 4095) 2739 var eqx: i64 = 0 2740 if rvcx == 3 { if n1x == n3x { if n1x > 0 { 2741 eqx = 1 2742 var ci: i64 = 0 2743 while ci < n1x { if s1x[ci] != s3x[ci] { eqx = 0; ci = n1x } ci = ci + 1 } 2744 } } } 2745 var t11dok: i64 = 0 2746 if t11d == 1 { if eqx == 1 { t11dok = 1 } } 2747 of_ck("T11d restore v1 -> v3==v1 spec, additive (now 3 versions)" as *u8, t11dok, pass, fail) 2748 // T12 the PRODUCT SHELL (U-axes rung): search / type chips / sort order / REAL thumbnails / empty states / 2749 // editor chrome. Ordering asserted with of_findpos, not just presence. 2750 n = of_handle(root, base, "GET /?q=budget HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 47, out, 1048576) 2751 var t12a: i64 = 0 2752 if of_memhas(out, n, "family-budget" as *u8) == 1 { if of_memhas(out, n, "/doc/family-letter" as *u8) == 0 { t12a = 1 } } 2753 of_ck("T12a search q=budget filters the card grid" as *u8, t12a, pass, fail) 2754 n = of_handle(root, base, "GET /?type=deck HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 48, out, 1048576) 2755 var t12b: i64 = 0 2756 if of_memhas(out, n, "reunion-deck" as *u8) == 1 { if of_memhas(out, n, "/doc/family-budget" as *u8) == 0 { t12b = 1 } } 2757 of_ck("T12b type=deck chip filters to decks only" as *u8, t12b, pass, fail) 2758 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2759 var pd: i64 = of_findpos(out, n, "/doc/reunion-deck" as *u8) 2760 var plt: i64 = of_findpos(out, n, "/doc/family-letter" as *u8) 2761 var t12c: i64 = 0 2762 if pd >= 0 { if plt >= 0 { if pd < plt { t12c = 1 } } } 2763 of_ck("T12c default sort = recently edited first (deck before letter)" as *u8, t12c, pass, fail) 2764 n = of_handle(root, base, "GET /?sort=name HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 48, out, 1048576) 2765 var pb2: i64 = of_findpos(out, n, "/doc/family-budget" as *u8) 2766 var pl2: i64 = of_findpos(out, n, "/doc/family-letter" as *u8) 2767 var pd2: i64 = of_findpos(out, n, "/doc/reunion-deck" as *u8) 2768 var t12d: i64 = 0 2769 if pb2 >= 0 { if pl2 > pb2 { if pd2 > pl2 { t12d = 1 } } } 2770 of_ck("T12d sort=name orders the grid alphabetically" as *u8, t12d, pass, fail) 2771 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2772 var t12e: i64 = 0 2773 if of_memhas(out, n, "class='thumb'" as *u8) == 1 { if of_memhas(out, n, "tsheet" as *u8) == 1 { if of_memhas(out, n, "Apples" as *u8) == 1 { if of_memhas(out, n, "Family Letter EDITED" as *u8) == 1 { t12e = 1 } } } } 2774 of_ck("T12e home cards carry REAL content thumbnails (sheet cells + doc heading)" as *u8, t12e, pass, fail) 2775 n = of_handle(root, base, "GET /?q=zzzz HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 45, out, 1048576) 2776 of_ck("T12f no-match search shows an empty state" as *u8, of_memhas(out, n, "No files match" as *u8), pass, fail) 2777 let root2: *u8 = sys_mmap(600) 2778 var r2o: i64 = of_cat(root2, 0, root); r2o = of_cat(root2, r2o, "-fresh" as *u8); root2[r2o] = 0 as u8 2779 n = of_handle(root2, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2780 of_ck("T12g first-run onboarding empty state" as *u8, of_memhas(out, n, "Create your first document" as *u8), pass, fail) 2781 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2782 var t12h: i64 = 0 2783 if of_memhas(out, n, "Saved v2" as *u8) == 1 { if of_memhas(out, n, "data-act='u'" as *u8) == 1 { if of_memhas(out, n, "data-act='find'" as *u8) == 1 { if of_memhas(out, n, "id='savest'" as *u8) == 1 { t12h = 1 } } } } 2784 of_ck("T12h editor carries saved-stamp + undo/find controls + save-state slot" as *u8, t12h, pass, fail) 2785 var nj: i64 = of_handle(root, base, "GET /app.js HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 44, out, 1048576) 2786 var t12i: i64 = 0 2787 if of_memhas(out, nj, "ctrlKey" as *u8) == 1 { if of_memhas(out, nj, "createTreeWalker" as *u8) == 1 { if of_memhas(out, nj, "localStorage" as *u8) == 1 { t12i = 1 } } } 2788 of_ck("T12i app.js ships find/shortcuts/theme (ctrlKey+TreeWalker+localStorage)" as *u8, t12i, pass, fail) 2789 // T13 TRUE AUTOSAVE (U4 rung): ajax=1 -> 200 OK v<N> (no 303 navigation), manifest tags the version with 2790 // the additive 4th field "auto", and the Versions table labels it. The JS side (debounce + changed-only) 2791 // ships in app.js (T13d needles) -- browser timing itself is not headless-executed here (honest caveat). 2792 bo = of_cat(bod, 0, "name=family-letter&kind=doc&spec=H+Family+Letter+AUTO%0AP+Background+save.&ajax=1" as *u8) 2793 bod[bo] = 0 as u8 2794 ro = of_cat(req, 0, "POST /save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2795 ro = of_catn(req, ro, bo) 2796 ro = of_cat(req, ro, "\r\n\r\n" as *u8) 2797 ro = of_cat(req, ro, bod) 2798 n = of_handle(root, base, req, ro, out, 1048576) 2799 var t13a: i64 = 0 2800 if of_memhas(out, n, "200 OK" as *u8) == 1 { if of_memhas(out, n, "OK v3" as *u8) == 1 { if of_memhas(out, n, "303" as *u8) == 0 { t13a = 1 } } } 2801 of_ck("T13a ajax autosave answers 200 OK v3 (no 303)" as *u8, t13a, pass, fail) 2802 var t13b: i64 = 0 2803 if of_manifest_is_auto(root, "family-letter" as *u8, 3) == 1 { if of_manifest_is_auto(root, "family-letter" as *u8, 2) == 0 { t13b = 1 } } 2804 of_ck("T13b manifest tags v3 auto, v2 not" as *u8, t13b, pass, fail) 2805 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2806 var t13c: i64 = 0 2807 if of_memhas(out, n, "(auto)" as *u8) == 1 { if of_memhas(out, n, "Save as v4" as *u8) == 1 { t13c = 1 } } 2808 of_ck("T13c versions table labels the autosave + next save renumbers v4" as *u8, t13c, pass, fail) 2809 var nj2: i64 = of_handle(root, base, "GET /app.js HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 44, out, 1048576) 2810 var t13d: i64 = 0 2811 if of_memhas(out, nj2, "ajax=1" as *u8) == 1 { if of_memhas(out, nj2, "lastSpec" as *u8) == 1 { if of_memhas(out, nj2, "Autosaved v" as *u8) == 1 { t13d = 1 } } } 2812 of_ck("T13d app.js ships the autosave engine (ajax=1 + changed-only lastSpec)" as *u8, t13d, pass, fail) 2813 // T14 HTML pages carry Content-Length so the STRICT sovereign browser can frame them (the /office in-browser 2814 // fetch failed without it). Assert present on the home response AND that its value == the actual body length. 2815 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2816 var t14: i64 = 0 2817 if of_memhas(out, n, "Content-Length: " as *u8) == 1 { 2818 var hp: i64 = 0 - 1 2819 var xi: i64 = 0 2820 while xi + 3 < n { if out[xi] == (13 as u8) { if out[xi+1] == (10 as u8) { if out[xi+2] == (13 as u8) { if out[xi+3] == (10 as u8) { hp = xi; xi = n } } } } xi = xi + 1 } 2821 if hp >= 0 { 2822 let bodylen: i64 = n - (hp + 4) 2823 var cl: i64 = 0 2824 var found: i64 = 0 2825 var ci: i64 = 0 2826 let key: *u8 = "Content-Length: " as *u8 2827 while ci + 16 < hp { 2828 if of_starts((out as i64 + ci) as *u8, key) == 1 { 2829 var t: i64 = ci + 16 2830 while t < hp { let dch: i64 = out[t] as i64; if dch >= 48 { if dch <= 57 { cl = cl * 10 + (dch - 48); found = 1 } } if out[t] == (13 as u8) { t = hp } else { t = t + 1 } } 2831 ci = hp 2832 } else { ci = ci + 1 } 2833 } 2834 if found == 1 { if cl == bodylen { t14 = 1 } } 2835 } 2836 } 2837 of_ck("T14 home carries a CORRECT Content-Length (strict-client framable)" as *u8, t14, pass, fail) 2838 2839 // ---- AUTHZ (nx_rebac): owner-binding + read/write gating + share/unshare -- the office integration ---- 2840 let azp: *u8 = sys_mmap(96); var azo: i64 = of_cat(azp, 0, "/tmp/ofauthz_" as *u8); azo = of_catn(azp, azo, sys_now_us()); azo = of_cat(azp, azo, "_" as *u8); azp[azo] = 0 as u8 2841 let azal: *u8 = sys_mmap(24); of_meref(azal, "alice" as *u8) 2842 let azbo: *u8 = sys_mmap(24); of_meref(azbo, "bob" as *u8) 2843 let azca: *u8 = sys_mmap(24); of_meref(azca, "carol" as *u8) 2844 let azempty: *u8 = sys_mmap(4); azempty[0] = 0 as u8 2845 of_ck("AZ0a unowned doc PUBLIC to authed (non-breaking)" as *u8, of_read_ok(azp, azal, 1, "flyer" as *u8), pass, fail) 2846 of_ck("AZ0b unowned doc PUBLIC to anon (non-breaking)" as *u8, of_read_ok(azp, azempty, 0, "flyer" as *u8), pass, fail) 2847 let azsref: *u8 = sys_mmap(32); of_docref(azsref, "secret" as *u8) 2848 rb_put(azp, azsref, "owner" as *u8, azal, "alice" as *u8, 1) 2849 of_ck("AZ1 owner alice reads owned secret" as *u8, of_read_ok(azp, azal, 1, "secret" as *u8), pass, fail) 2850 of_ck("AZ2 anon DENIED on owned secret" as *u8, 1 - of_read_ok(azp, azempty, 0, "secret" as *u8), pass, fail) 2851 of_ck("AZ3 bob (no grant) DENIED read" as *u8, 1 - of_read_ok(azp, azbo, 1, "secret" as *u8), pass, fail) 2852 of_ck("AZ4 bob DENIED write" as *u8, 1 - of_write_ok(azp, azbo, 1, "secret" as *u8), pass, fail) 2853 rb_put(azp, azsref, "viewer" as *u8, azbo, "alice" as *u8, 1) 2854 of_ck("AZ5 bob now READS (shared viewer)" as *u8, of_read_ok(azp, azbo, 1, "secret" as *u8), pass, fail) 2855 of_ck("AZ6 bob viewer still cannot WRITE" as *u8, 1 - of_write_ok(azp, azbo, 1, "secret" as *u8), pass, fail) 2856 rb_put(azp, azsref, "editor" as *u8, azca, "alice" as *u8, 1) 2857 of_ck("AZ7 carol editor CAN write" as *u8, of_write_ok(azp, azca, 1, "secret" as *u8), pass, fail) 2858 rb_put(azp, azsref, "viewer" as *u8, azbo, "alice" as *u8, 0) 2859 of_ck("AZ8 bob REVOKED (unshare tombstone)" as *u8, 1 - of_read_ok(azp, azbo, 1, "secret" as *u8), pass, fail) 2860 of_ck("AZ9 alice may_grant (owner)" as *u8, rb_may_grant(azp, azal, azsref), pass, fail) 2861 of_ck("AZ10 bob may_grant DENIED (not owner)" as *u8, 1 - rb_may_grant(azp, azbo, azsref), pass, fail) 2862 let azdob: *u8 = sys_mmap(8192) 2863 let azsh1: *u8 = "name=secret&who=dan&level=viewer" as *u8 2864 let azr1: i64 = of_do_share("/office" as *u8, azp, azal, "alice" as *u8, 1, azsh1, of_slen(azsh1), azdob, 1) 2865 let azdan: *u8 = sys_mmap(16); of_meref(azdan, "dan" as *u8) 2866 of_ck("AZ11a of_do_share owner->dan = 303" as *u8, of_memhas(azdob, azr1, "303" as *u8), pass, fail) 2867 of_ck("AZ11b dan now reads secret" as *u8, of_read_ok(azp, azdan, 1, "secret" as *u8), pass, fail) 2868 let azr2: i64 = of_do_share("/office" as *u8, azp, azbo, "bob" as *u8, 1, azsh1, of_slen(azsh1), azdob, 1) 2869 of_ck("AZ12 of_do_share by non-owner bob = 403" as *u8, of_memhas(azdob, azr2, "403" as *u8), pass, fail) 2870 let azsh2: *u8 = "name=secret&who=ev:il&level=viewer" as *u8 2871 let azr3: i64 = of_do_share("/office" as *u8, azp, azal, "alice" as *u8, 1, azsh2, of_slen(azsh2), azdob, 1) 2872 of_ck("AZ13 of_do_share bad handle = 400 (no forge)" as *u8, of_memhas(azdob, azr3, "400" as *u8), pass, fail) 2873 let azmob: *u8 = sys_mmap(65536) 2874 let azm1: i64 = of_managepage("/office" as *u8, azp, azal, 1, "secret" as *u8, "TOK" as *u8, azmob, 65536) 2875 of_ck("AZ14 owner manage page renders" as *u8, of_memhas(azmob, azm1, "Sharing: secret" as *u8), pass, fail) 2876 let azm2: i64 = of_managepage("/office" as *u8, azp, azbo, 1, "secret" as *u8, "TOK" as *u8, azmob, 65536) 2877 var azm2ok: i64 = 0 2878 if azm2 == (0 - 3) { azm2ok = 1 } 2879 of_ck("AZ15 non-owner manage DENIED (-3)" as *u8, azm2ok, pass, fail) 2880 let azm3: i64 = of_managepage("/office" as *u8, azp, azal, 1, "flyer" as *u8, "TOK" as *u8, azmob, 65536) 2881 var azm3ok: i64 = 0 2882 if azm3 == (0 - 2) { azm3ok = 1 } 2883 of_ck("AZ16 manage unowned = not-owned (-2)" as *u8, azm3ok, pass, fail) 2884 of_ck("AZ17 handle validator rejects colon (no type forge)" as *u8, 1 - of_tok_ok("ev:il" as *u8), pass, fail) 2885 of_ck("AZ18 handle validator accepts ok_h-1" as *u8, of_tok_ok("ok_h-1" as *u8), pass, fail) 2886 2887 // ---- GROUP SHARING (the userset differentiator): share a doc with a GROUP, members inherit access ---- 2888 let azgadd: *u8 = "group=team&who=rose" as *u8 2889 let azgr: i64 = of_do_group("/office" as *u8, azp, azal, "alice" as *u8, 1, azgadd, of_slen(azgadd), azdob, 1) 2890 of_ck("AZ19 alice creates group team + adds rose = 303" as *u8, of_memhas(azdob, azgr, "303" as *u8), pass, fail) 2891 let azgshare: *u8 = "name=secret&who=team&level=viewer&target=group" as *u8 2892 let azgs: i64 = of_do_share("/office" as *u8, azp, azal, "alice" as *u8, 1, azgshare, of_slen(azgshare), azdob, 1) 2893 of_ck("AZ20 share secret with GROUP team = 303" as *u8, of_memhas(azdob, azgs, "303" as *u8), pass, fail) 2894 let azrose: *u8 = sys_mmap(16); of_meref(azrose, "rose" as *u8) 2895 of_ck("AZ21 rose reads secret VIA GROUP membership (userset)" as *u8, of_read_ok(azp, azrose, 1, "secret" as *u8), pass, fail) 2896 // reverse index (list-objects): what docs can each subject reach? 2897 let azmd: *i64 = sys_mmap(8 * 128) as *i64 2898 let aznd: i64 = of_my_docs(azp, azrose, azmd, 128) 2899 of_ck("AZ21b reverse-index: rose's docs include secret VIA GROUP" as *u8, rb_in_list(azmd, aznd, "doc:secret" as *u8), pass, fail) 2900 let azmd2: *i64 = sys_mmap(8 * 128) as *i64 2901 let aznd2: i64 = of_my_docs(azp, azal, azmd2, 128) 2902 of_ck("AZ21c reverse-index: alice's docs include her owned secret" as *u8, rb_in_list(azmd2, aznd2, "doc:secret" as *u8), pass, fail) 2903 let azfrank: *u8 = sys_mmap(16); of_meref(azfrank, "frank" as *u8) 2904 of_ck("AZ22 frank (not in team) DENIED" as *u8, 1 - of_read_ok(azp, azfrank, 1, "secret" as *u8), pass, fail) 2905 let azgrem: *u8 = "group=team&who=rose" as *u8 2906 of_do_group("/office" as *u8, azp, azal, "alice" as *u8, 1, azgrem, of_slen(azgrem), azdob, 0) 2907 of_ck("AZ23 remove rose from team -> loses secret (userset re-eval)" as *u8, 1 - of_read_ok(azp, azrose, 1, "secret" as *u8), pass, fail) 2908 let azbob2: *u8 = "group=team&who=x" as *u8 2909 let azgb: i64 = of_do_group("/office" as *u8, azp, azbo, "bob" as *u8, 1, azbob2, of_slen(azbob2), azdob, 1) 2910 of_ck("AZ24 non-owner bob cannot add to team = 403 (no group hijack)" as *u8, of_memhas(azdob, azgb, "403" as *u8), pass, fail) 2911 2912 // ---- AI CONTINUE (in-app AI over our own 0.5B seat): offline gate proves the button + graceful 503 ---- 2913 // T-AI-a: the doc editor page offers the "Continue with AI" form (doc kind). 2914 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2915 var tai_a: i64 = 0 2916 if of_memhas(out, n, "Continue with AI" as *u8) == 1 { if of_memhas(out, n, "/ai'><input type='hidden' name='name' value='family-letter'" as *u8) == 1 { tai_a = 1 } } 2917 of_ck("T-AI-a doc page offers the AI-continue form (our own 0.5B model)" as *u8, tai_a, pass, fail) 2918 // T-AI-b: POST /office/ai with the LLM seat DOWN (offline gate) -> graceful 503, no version created, no crash. 2919 let aicnt0: *u8 = sys_mmap(64) 2920 let aiv0: i64 = of_manifest_count(root, "family-letter" as *u8, aicnt0, 64) 2921 bo = of_cat(bod, 0, "name=family-letter" as *u8); bod[bo] = 0 as u8 2922 ro = of_cat(req, 0, "POST /office/ai HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2923 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2924 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 2925 let aicnt1: *u8 = sys_mmap(64) 2926 let aiv1: i64 = of_manifest_count(root, "family-letter" as *u8, aicnt1, 64) 2927 var tai_b: i64 = 0 2928 if of_memhas(out, n, "503" as *u8) == 1 { if aiv1 == aiv0 { tai_b = 1 } } 2929 of_ck("T-AI-b POST /ai with seat DOWN -> graceful 503 (framed) + NO version created" as *u8, tai_b, pass, fail) 2930 // ---- U4 OFFLINE/PWA + SKELETONS + A11Y ---- 2931 // T-U4-a: the web app manifest is served and installable (name + start_url). 2932 n = of_handle(root, base, "GET /manifest.webmanifest HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 59, out, 1048576) 2933 var tu4_a: i64 = 0 2934 if of_memhas(out, n, "200 OK" as *u8) == 1 { if of_memhas(out, n, "Nishi Office" as *u8) == 1 { if of_memhas(out, n, "standalone" as *u8) == 1 { tu4_a = 1 } } } 2935 of_ck("T-U4-a /manifest.webmanifest serves an installable PWA manifest" as *u8, tu4_a, pass, fail) 2936 // T-U4-b: the service worker is served AT THE MOUNT with Service-Worker-Allowed, else its scope cannot 2937 // cover the app it is meant to make offline-capable. 2938 n = of_handle(root, base, "GET /sw.js HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 44, out, 1048576) 2939 var tu4_b: i64 = 0 2940 if of_memhas(out, n, "200 OK" as *u8) == 1 { if of_memhas(out, n, "Service-Worker-Allowed" as *u8) == 1 { tu4_b = 1 } } 2941 of_ck("T-U4-b /sw.js serves the worker with Service-Worker-Allowed" as *u8, tu4_b, pass, fail) 2942 // T-U4-c: the worker must NEVER cache document content -- a never-lose-versions app showing a stale doc 2943 // would be the one lie its whole design forbids. Assert the exclusion is actually in the shipped worker. 2944 let swb: *u8 = sys_mmap(65536) 2945 let swl: i64 = of_read_asset("office_sw.js" as *u8, swb, 65535) 2946 var tu4_c: i64 = 0 2947 if swl > 0 { if of_memhas(swb, swl, "/doc/" as *u8) == 1 { if of_memhas(swb, swl, ".method !== 'GET'" as *u8) == 1 { tu4_c = 1 } } } 2948 of_ck("T-U4-c the worker EXCLUDES /doc/ content and never touches a write" as *u8, tu4_c, pass, fail) 2949 // T-U4-d: shell head advertises the manifest + theme colour, and the skeleton style exists. 2950 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 39, out, 1048576) 2951 var tu4_d: i64 = 0 2952 if of_memhas(out, n, "rel='manifest'" as *u8) == 1 { if of_memhas(out, n, "theme-color" as *u8) == 1 { if of_memhas(out, n, ".skel{" as *u8) == 1 { tu4_d = 1 } } } 2953 of_ck("T-U4-d shell advertises the manifest + theme-color and ships skeleton styles" as *u8, tu4_d, pass, fail) 2954 // ---- U3 LIBRARY ORGANISATION: starred + folders + shared-with-me ---- 2955 // T-U3-a: star toggles ON, is visible in the library under ?filter=starred, and toggles back OFF. 2956 bo = of_cat(bod, 0, "name=family-letter" as *u8); bod[bo] = 0 as u8 2957 ro = of_cat(req, 0, "POST /office/star HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2958 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2959 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 2960 var tu3_a: i64 = 0 2961 if of_starred(root, "family-letter" as *u8) == 1 { 2962 n = of_handle(root, base, "GET /?filter=starred HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 53, out, 1048576) 2963 if of_memhas(out, n, "family-letter" as *u8) == 1 { tu3_a = 1 } 2964 } 2965 of_ck("T-U3-a star ON and the starred filter shows the file" as *u8, tu3_a, pass, fail) 2966 // T-U3-b: the starred filter must EXCLUDE an unstarred file -- a filter that shows everything is not a filter. 2967 var tu3_b: i64 = 0 2968 if of_memhas(out, n, "family-budget" as *u8) == 0 { tu3_b = 1 } 2969 of_ck("T-U3-b starred filter EXCLUDES unstarred files (non-vacuous)" as *u8, tu3_b, pass, fail) 2970 // T-U3-c: folder set, then the folder filter selects it and rejects a different folder. 2971 bo = of_cat(bod, 0, "name=family-letter&folder=reunion" as *u8); bod[bo] = 0 as u8 2972 ro = of_cat(req, 0, "POST /office/folder HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2973 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2974 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 2975 let fchk: *u8 = sys_mmap(64) 2976 of_folder_get(root, "family-letter" as *u8, fchk, 64) 2977 var tu3_c: i64 = 0 2978 if of_seq(fchk, "reunion" as *u8) == 1 { 2979 n = of_handle(root, base, "GET /?folder=reunion HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 53, out, 1048576) 2980 if of_memhas(out, n, "family-letter" as *u8) == 1 { if of_memhas(out, n, "family-budget" as *u8) == 0 { tu3_c = 1 } } 2981 } 2982 of_ck("T-U3-c folder set + folder filter selects only that folder" as *u8, tu3_c, pass, fail) 2983 // T-U3-d: a folder label carrying separators or markup is REFUSED (it becomes a URL filter value). 2984 bo = of_cat(bod, 0, "name=family-letter&folder=a/b" as *u8); bod[bo] = 0 as u8 2985 ro = of_cat(req, 0, "POST /office/folder HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 2986 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 2987 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 2988 of_ck("T-U3-d a folder label with a separator is LOUDLY refused" as *u8, of_memhas(out, n, "400" as *u8), pass, fail) 2989 // T-U3-e: the doc page offers both controls. 2990 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 2991 var tu3_e: i64 = 0 2992 if of_memhas(out, n, "Organise" as *u8) == 1 { if of_memhas(out, n, "/star'" as *u8) == 1 { if of_memhas(out, n, "/folder'" as *u8) == 1 { tu3_e = 1 } } } 2993 of_ck("T-U3-e doc page offers star + folder controls" as *u8, tu3_e, pass, fail) 2994 // ---- TEMPLATES GALLERY (U3): real starter content, not an empty page ---- 2995 // T-TM-a: the home page offers the gallery and its templates carry WORKING content, not lorem text -- the 2996 // budget must ship a live =SUM and a #CF rule, else it demonstrates nothing about the engine underneath. 2997 n = of_handle(root, base, "GET / HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 38, out, 1048576) 2998 var ttm_a: i64 = 0 2999 if of_memhas(out, n, "<h2>Templates</h2>" as *u8) == 1 { 3000 if of_memhas(out, n, "Meeting agenda" as *u8) == 1 { 3001 if of_memhas(out, n, "Monthly budget" as *u8) == 1 { 3002 if of_memhas(out, n, "=SUM(B2:B3)" as *u8) == 1 { if of_memhas(out, n, "#CF B2:B3 greaterThan 300" as *u8) == 1 { ttm_a = 1 } } 3003 } 3004 } 3005 } 3006 of_ck("T-TM-a home offers Templates and the budget carries a REAL formula + CF rule" as *u8, ttm_a, pass, fail) 3007 // T-TM-b: a template POSTs to the SAME /save contract, so it becomes an ORDINARY versioned file. 3008 bo = of_cat(bod, 0, "name=tmpl-check&kind=doc&spec=H+Letter%0AP+Dear+friend%2C" as *u8); bod[bo] = 0 as u8 3009 ro = of_cat(req, 0, "POST /office/save HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 3010 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 3011 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 3012 let tmk: *u8 = sys_mmap(64) 3013 let tmv: i64 = of_manifest_count(root, "tmpl-check" as *u8, tmk, 64) 3014 var ttm_b: i64 = 0 3015 if of_memhas(out, n, "303" as *u8) == 1 { if tmv == 1 { ttm_b = 1 } } 3016 of_ck("T-TM-b a template lands as an ORDINARY v1 file (same /save contract)" as *u8, ttm_b, pass, fail) 3017 // ---- OF-A2 (sheet insight) + OF-A3 (deck slide): the AI reaches EVERY kind, in that kind's own grammar ---- 3018 // T-AI-c: the SHEET page offers the insight card (not the doc wording) -- proves the kind branch renders. 3019 n = of_handle(root, base, "GET /doc/family-budget HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 3020 var tai_c: i64 = 0 3021 if of_memhas(out, n, "Add an AI insight" as *u8) == 1 { if of_memhas(out, n, "Continue with AI" as *u8) == 0 { tai_c = 1 } } 3022 of_ck("T-AI-c SHEET page offers the AI-insight card (and NOT the doc wording)" as *u8, tai_c, pass, fail) 3023 // T-AI-d: the DECK page offers the slide card. 3024 n = of_handle(root, base, "GET /doc/reunion-deck HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 55, out, 1048576) 3025 var tai_d: i64 = 0 3026 if of_memhas(out, n, "Draft the next slide" as *u8) == 1 { if of_memhas(out, n, "Add an AI insight" as *u8) == 0 { tai_d = 1 } } 3027 of_ck("T-AI-d DECK page offers the AI-slide card (and NOT the sheet wording)" as *u8, tai_d, pass, fail) 3028 // T-AI-e: POST /ai on a SHEET is now a REAL route (no longer the old doc-only 400) and degrades gracefully. 3029 let aik0: *u8 = sys_mmap(64) 3030 let aic0: i64 = of_manifest_count(root, "family-budget" as *u8, aik0, 64) 3031 bo = of_cat(bod, 0, "name=family-budget" as *u8); bod[bo] = 0 as u8 3032 ro = of_cat(req, 0, "POST /office/ai HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 3033 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 3034 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 3035 let aik1: *u8 = sys_mmap(64) 3036 let aic1: i64 = of_manifest_count(root, "family-budget" as *u8, aik1, 64) 3037 var tai_e: i64 = 0 3038 if of_memhas(out, n, "503" as *u8) == 1 { if of_memhas(out, n, "400" as *u8) == 0 { if aic1 == aic0 { tai_e = 1 } } } 3039 of_ck("T-AI-e POST /ai on a SHEET -> 503 not 400 (kind is wired), NO version created" as *u8, tai_e, pass, fail) 3040 // ---- OF-A4 AGENT MODE (offline gate) -- the state machine + plan parser are fully provable with the seat 3041 // DOWN, so these teeth run everywhere. Only the model's WORDS need a live seat; the agency does not. 3042 // T-AG-a: the doc page offers agent mode with a goal input. 3043 n = of_handle(root, base, "GET /doc/family-letter HTTP/1.1\r\nHost: office.local\r\n\r\n" as *u8, 56, out, 1048576) 3044 var tag_a: i64 = 0 3045 if of_memhas(out, n, "Agent mode" as *u8) == 1 { if of_memhas(out, n, "/agent'><input type='hidden' name='name' value='family-letter'" as *u8) == 1 { tag_a = 1 } } 3046 of_ck("T-AG-a doc page offers agent mode with a goal input" as *u8, tag_a, pass, fail) 3047 // T-AG-b: POST /agent with a goal + seat DOWN -> graceful 503, NO version, NO agent state written. 3048 let agk0: *u8 = sys_mmap(64) 3049 let agc0: i64 = of_manifest_count(root, "family-letter" as *u8, agk0, 64) 3050 bo = of_cat(bod, 0, "name=family-letter&goal=add+a+closing" as *u8); bod[bo] = 0 as u8 3051 ro = of_cat(req, 0, "POST /office/agent HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 3052 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 3053 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 3054 let agk1: *u8 = sys_mmap(64) 3055 let agc1: i64 = of_manifest_count(root, "family-letter" as *u8, agk1, 64) 3056 let agchk: *u8 = sys_mmap(8192) 3057 let agchkn: i64 = of_agent_read(root, "family-letter" as *u8, agchk, 8000) 3058 var tag_b: i64 = 0 3059 if of_memhas(out, n, "503" as *u8) == 1 { if agc1 == agc0 { if agchkn <= 0 { tag_b = 1 } } } 3060 of_ck("T-AG-b POST /agent seat DOWN -> 503, NO version, NO agent state (retry-safe)" as *u8, tag_b, pass, fail) 3061 // T-AG-c: no goal and no plan yet -> LOUD 400, never a silent no-op. 3062 bo = of_cat(bod, 0, "name=family-letter" as *u8); bod[bo] = 0 as u8 3063 ro = of_cat(req, 0, "POST /office/agent HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 3064 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 3065 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 3066 of_ck("T-AG-c POST /agent with no goal -> LOUD 400" as *u8, of_memhas(out, n, "400" as *u8), pass, fail) 3067 // T-AG-d: agent mode is doc-only -> a SHEET is refused loudly (the kind guard actually bites). 3068 bo = of_cat(bod, 0, "name=family-budget&goal=summarise" as *u8); bod[bo] = 0 as u8 3069 ro = of_cat(req, 0, "POST /office/agent HTTP/1.1\r\nHost: office.local\r\nContent-Length: " as *u8) 3070 ro = of_catn(req, ro, bo); ro = of_cat(req, ro, "\r\n\r\n" as *u8); ro = of_cat(req, ro, bod) 3071 n = of_handle(root, "/office" as *u8, req, ro, out, 1048576) 3072 of_ck("T-AG-d POST /agent on a SHEET -> LOUD 400 (doc-kind guard bites)" as *u8, of_memhas(out, n, "400" as *u8), pass, fail) 3073 // T-AG-e: the plan parser turns a real multi-line model reply into ordered steps, list punctuation stripped. 3074 let agpl: *u8 = sys_mmap(4096) 3075 let agsrc: *u8 = "1. Draft the summer plans section\n2) Add a warm closing paragraph\n- Check the tone\n" as *u8 3076 let agns: i64 = of_agent_plan_parse(agsrc, of_slen(agsrc), agpl, 4000) 3077 var tag_e: i64 = 0 3078 if agns == 3 { if of_memhas(agpl, of_slen(agpl), "S Draft the summer plans section" as *u8) == 1 { if of_memhas(agpl, of_slen(agpl), "S Add a warm closing paragraph" as *u8) == 1 { tag_e = 1 } } } 3079 of_ck("T-AG-e plan parser -> 3 ordered steps, list punctuation stripped" as *u8, tag_e, pass, fail) 3080 // T-AG-f: a SINGLE-LINE reply still becomes a real plan (sentence-split fallback), not a 1-step degenerate. 3081 let agpl2: *u8 = sys_mmap(4096) 3082 let agsrc2: *u8 = "Write the intro. Then add the plans. Finally close it." as *u8 3083 let agns2: i64 = of_agent_plan_parse(agsrc2, of_slen(agsrc2), agpl2, 4000) 3084 var tag_f: i64 = 0 3085 if agns2 >= 3 { tag_f = 1 } 3086 of_ck("T-AG-f single-line reply -> sentence-split into a multi-step plan" as *u8, tag_f, pass, fail) 3087 // T-AG-g: garbage in -> NO plan (the parser refuses rather than inventing a step) = the liar-kill tooth. 3088 let agpl3: *u8 = sys_mmap(4096) 3089 let agns3: i64 = of_agent_plan_parse("..\n-\n" as *u8, 5, agpl3, 4000) 3090 var tag_g: i64 = 0 3091 if agns3 == 0 { tag_g = 1 } 3092 of_ck("T-AG-g unusable model reply -> 0 steps (refuses, never invents)" as *u8, tag_g, pass, fail) 3093 // T-AG-h: state accessors round-trip (goal, ordered steps, done cursor) through the sidecar format. 3094 let agst: *u8 = "G ship it\nS one\nS two\nD 1\n" as *u8 3095 let agstn: i64 = of_slen(agst) 3096 let agtmp: *u8 = sys_mmap(256) 3097 var tag_h: i64 = 0 3098 if of_agent_tagcount(agst, agstn, 83) == 2 { 3099 if of_agent_done(agst, agstn) == 1 { 3100 of_agent_tagline(agst, agstn, 83, 1, agtmp, 256) 3101 if of_seq(agtmp, "two" as *u8) == 1 { tag_h = 1 } 3102 } 3103 } 3104 of_ck("T-AG-h agent state round-trips (2 steps, cursor=1, step[1]='two')" as *u8, tag_h, pass, fail) 3105 p("NX-OFFICE SELFTEST pass=" as *u8); pn(pass[0]); p(" fail=" as *u8); pn(fail[0]) 3106 if fail[0] == 0 { p(" verdict=GREEN (the office round-trips: create/edit/version/preview/download, additive by construction)\n" as *u8); return 0 } 3107 p(" verdict=RED\n" as *u8) 3108 return 1 3109}