nx_grab_ext_emit.nx
buildroot/runtime/nx_grab_ext_emit.nx
about
nx_grab_ext_emit.nx -- /compare/mediaingest R10 (contract symbol ge_emit): EMIT the browser extension
from data, for BOTH engine families, so Nishi OS and the Nishi browser and every third-party browser get
the same capture surface without a native companion.
OPERATOR STANDING ORDER 2026-08-30: "i dont want js powering any of this -- emit from the first byte up via
nishi lang." A WebExtension is JavaScript by the browser's own definition and no organ can change that. What
this organ changes is WHO WROTE IT: every byte the browser runs is emitted here from knowledge/grab_ext.conf,
so adding a media type or moving the edge host is a DATA edit that reproduces both twins, not a hand-edit of
two hand-written trees that then drift. That is the same reason nx_gen_ui_emit exists for the /gen page.
WHY BOTH TWINS COME FROM ONE FILE. MV2 (Firefox/Waterfox) and MV3 (Chrome/Edge) differ in STRUCTURE:
background.scripts vs background.service_worker
browser_action vs action
permissions carries <all_urls> vs host_permissions carries it
They must NOT differ in POLICY. Emitting both from the same rows makes divergence impossible by construction
instead of by review discipline -- the estate's standing rule when two artifacts must agree.
WHY NO NATIVE COMPANION (this is R11 ge_edge_push, and it is a DELIBERATE non-dependency). The incumbent
(Video DownloadHelper) shipped a native CoApp for years and its own README now says "VDH CoApp is not being
developed anymore -- VDH v10 doesn't require a companion." The field moved off native messaging; we do not
re-adopt it. The extension holds no binary and does no muxing: it SNIFFS in the tab and POSTs one URL to the
estate over the authenticated edge, and the NAS does the capture. That is why it works on Windows with no
WSL and no installed binary.
⛔ DECLARED IMPRECISION, NAMED BECAUSE AN UNDECLARED ONE IS A SILENT DEFECT: the emitted background body
holds its per-tab media list in a plain in-memory object. On MV2 that is correct -- the background page is
PERSISTENT. On MV3 the service worker is EPHEMERAL and is torn down when idle, so the list and the badge
count are lost on wake, and a user who leaves a tab open then clicks the toolbar button can see an empty
list on Chrome/Edge where Firefox/Waterfox shows the real one. The capture PUSH is unaffected (it carries
the url in the message), so this degrades discovery, never correctness of what is captured.
THE FIX IS chrome.storage.session MIRRORED ON EVERY ADD PLUS REHYDRATION ON WAKE, and it is deliberately
NOT done in this increment: it is a behaviour change to the shared body that both twins run, so it wants
its own teeth (wake-with-empty-memory rehydrates; MV2 unaffected) rather than being smuggled in beside the
structural split this increment exists to prove. Tracked as the next increment of R10.
⚠ nx_cc LEXER: a string literal may contain neither '#' nor '!'. The emitted JavaScript is therefore written
WITHOUT the '!' operator anywhere -- "=== false" and explicit comparisons instead of "!x" and "!==". This is
a real constraint on the emitted text, not a style choice, and a future edit that reaches for '!' will fail
to compile rather than fail silently.
dependencies 1 imports · 1 importers
imports: nx_syscalls.nx
imported by: nx_grab_ext_emit_gate.nx
structs
| none |
consts
| 47 | const GX_CONF_PATH: *u8 = "knowledge/grab_ext.conf" |
| 48 | const GX_CONF_CAP: i64 = 65536 |
| 49 | const GX_OUT_CAP: i64 = 262144 |
| 50 | const GX_VAL_CAP: i64 = 4096 |
| 51 | const GX_PATH_CAP: i64 = 1024 |
| 52 | const GX_DIR_MODE: i64 = 0x1FD // 0o775 |
| 53 | const GX_FILE_MODE: i64 = 0x1A4 // 0o644 |
| 55 | const GX_CH_NL: i64 = 10 |
| 56 | const GX_CH_CR: i64 = 13 |
| 57 | const GX_CH_PIPE: i64 = 124 |
| 58 | const GX_CH_HASH: i64 = 35 |
| 59 | const GX_CH_SP: i64 = 32 |
| 60 | const GX_CH_D0: i64 = 48 |
| 62 | const GX_EXIT_OK: i64 = 0 |
| 63 | const GX_EXIT_NOCONF: i64 = 2 |
| 64 | const GX_EXIT_MISSKEY: i64 = 3 |
| 65 | const GX_EXIT_BADEDGE: i64 = 4 |
| 66 | const GX_EXIT_WRITE: i64 = 5 |
| 67 | const GX_EXIT_OVERFLOW: i64 = 6 |
functions
| 69 | func gx_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 70 | func gx_w(s: *u8) -> i64 { sys_write(1, s, gx_slen(s)); return 0 } |
| 71 | func gx_wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 } |
| 72 | func gx_wn(v: i64) -> i64 |
| 84 | func gx_app(dst: *u8, k0: i64, src: *u8) -> i64 called by 5: gx_manifest_common_tailgx_emit_mv2gx_emit_mv3gx_emit_bggx_emit_content calls 1: gx_slen |
| 92 | func gx_appn(dst: *u8, k0: i64, src: *u8, n: i64) -> i64 |
| 108 | func gx_nth(buf: *u8, n: i64, key: *u8, idx: i64, off_out: *i64) -> i64 |
| 146 | func gx_count(buf: *u8, n: i64, key: *u8) -> i64 |
| 158 | func gx_need(buf: *u8, n: i64, key: *u8, off_out: *i64) -> i64 |
| 169 | func gx_starts(buf: *u8, off: i64, len: i64, lit: *u8) -> i64 calls 1: gx_slen |
| 181 | func gx_media_alt(conf: *u8, cn: i64, out: *u8, cap: i64) -> i64 |
| 201 | func gx_manifest_common_tail(out: *u8, k0: i64) -> i64 |
| 214 | func gx_emit_mv2(conf: *u8, cn: i64, out: *u8, cap: i64) -> i64 |
| 267 | func gx_emit_mv3(conf: *u8, cn: i64, out: *u8, cap: i64) -> i64 |
| 316 | func gx_emit_bg(conf: *u8, cn: i64, out: *u8, cap: i64) -> i64 |
| 379 | func gx_emit_content(conf: *u8, cn: i64, out: *u8, cap: i64) -> i64 |
| 403 | func gx_write_file(path: *u8, buf: *u8, n: i64) -> i64 |
| 416 | func gx_join(dir: *u8, leaf: *u8, out: *u8) -> i64 |
| 424 | func gx_emit_one(dir: *u8, leaf: *u8, body: *u8, n: i64) -> i64 |
| 442 | func ge_emit(root: *u8) -> i64 |
| 510 | func main(argc: i64, argv: *i64) -> i64 |