code wiki / (root) / nx_fsops.nx

nx_fsops.nx source

↩ module page · 168 lines · 9958 B

1// nx_fsops.nx -- CLI half of the consolidated filesystem tool (MCP name: nx_fs, tool #4 of the 15). 2// (Source named nx_fsops because nx_fs.nx is the file-I/O STDLIB.) READ-ONLY increment: 3// read <path> [maxbytes] [offset] -> bytes to stdout (truncation MARKED); DENY-LIST refuses secrets 4// lines <path> <start> [count] -> LINE-ADDRESSED window + declared envelope; composes with grep's 5// file:LINE output (the pair that byte-offset reads could not do) 6// tail <path> [count] -> the LAST <count> lines, answered from the file's own END in one call, with 7// a declared envelope (total_bytes, window_lines, last_line_terminated). Built 8// 2026-09-03 after a `lines` window chosen by hand was published as the end of 9// a log while its envelope said next=212. A WINDOW READ IS NOT A TAIL READ. 10// outline <path> -> ~2KB STRUCTURAL map (declaration/section lines) instead of dumping a 11// 50KB file; pair with `lines <path> <L> <count>` to pull the exact body 12// size <path> -> EXACT byte size in ONE call. This is the tail primitive: size, then 13// read <path> <n> <size-n>. Without it a caller hunting the end of a log 14// binary-searches offsets blind -- measured 2026-08-14, eight wasted calls 15// on a 2,508,190-byte log whose size this verb returns immediately. 16// mtime <path> -> mtime_sec/mtime_ns/now_sec/age_sec in ONE line (FUTURE-STAMPED if age<0); 17// implemented since 2026-08-07 and absent from this header until 2026-09-03 -- 18// the exact dark-verb class the paragraph below warns about 19// ls <dir> [skip] -> "<t> <name>" per entry (d/f/l/o); skip makes a dir past FSX_LS_CAP reachable 20// job <id> -> THE CLAIM-OR-OUT VERB (ES26, 2026-09-06): ONE call reads the job's terminal marker 21// (_jobs/job_<id>.claim) and, only when it says DONE, the output (.out). States NAMED: 22// NOSUCH (exit 3) / RUNNING (exit 8) / DONE / DONE-EMPTY / UNPARSED (exit 0) / 23// REFUSED (exit 2, the id must be digits). The estate's most common two-step ritual -- 24// marker then output, 120,106 adjacent pairs measured by nx_actlog steps -- made one step. 25// exit: 0 ok | 3 absent | 5 DENIED | 2 usage | 8 job RUNNING (codes surface in MCP _meta.exit_code) 26// ⚠THIS HEADER IS THE PUBLISHED CONTRACT. nx_toolgrammar harvests these lines verbatim into 27// knowledge/tool_grammar.conf, which nx_tools_api serves as the MCP call grammar -- so a verb missing 28// HERE is invisible to every caller even though the code implements it. `outline` and `size` were 29// exactly that: implemented, printed by the runtime usage string below, and absent from this header, 30// so tools/list advertised 3 of 5 verbs. ★A CAPABILITY THAT ITS OWN PUBLISHED GRAMMAR OMITS DOES NOT 31// EXIST TO ANY CALLER. Add the verb to BOTH this header and the usage line, or it ships dark. 32// license_tier: ORIGINAL 33import "nx_fsops_lib.nx" 34import "nx_fsops_outline.nx" 35import "nx_srcfresh.nx" // sf_mtime_ns -- ONE canonical mtime reader, not a fourth copy 36 37const FSC_USAGE_RC: i64 = 2 38const FSC_ARG_VERB: i64 = 1 // argv slot of the verb 39const FSC_ARG_P1: i64 = 2 // argv slot of the path 40const FSC_ARG_P2: i64 = 3 // argv slot of the optional maxbytes 41const FSC_ARGC_P1: i64 = 3 // argc with a path 42const FSC_ARGC_P2: i64 = 4 // argc with path + maxbytes 43const FSC_ASCII_9: i64 = 57 // '9' (decimal parse upper bound) 44const FSC_USAGE: *u8 = "usage: nx_fs read <path> [maxbytes] [offset] | lines <path> <start> [count] | tail <path> [count] | outline <path> | size <path> | mtime <path> | ls <dir> [skip] | job <id>\n" 45 46func fsc_atoi(s: *u8) -> i64 { 47 var v: i64 = 0 48 var i: i64 = 0 49 while s[i] != (0 as u8) { 50 let c: i64 = s[i] as i64 51 if c < FSX_ASCII_0 { return v } 52 if c > FSC_ASCII_9 { return v } 53 v = v * (10 as i64) + (c - FSX_ASCII_0) 54 i = i + 1 55 } 56 return v 57} 58 59// `size` DELEGATES to fsx_size in nx_fsops_lib.nx. The computation lives beside the other read verbs on 60// purpose: nx_fsops_gate drives the LIB directly, so a verb implemented only in this CLI half is a verb no 61// tooth can reach. A CAPABILITY THE GATE CANNOT CALL IS A CAPABILITY NOBODY IS DEFENDING. 62// MTIME, WITH THE CLOCK IT WAS READ AGAINST (2026-08-07). Built because a fleet freshness ruler went 63// unexplainably wrong and NOTHING in the estate could print a raw mtime: nx_gatefresh takes an organ NAME, 64// nx_staghyg prints mtimes only for elf pairs, and every other reader compares two stamps internally. 65// YOU CANNOT DEBUG A COMPARISON WHOSE INPUTS YOU CANNOT SEE. 66// OBSERVED, STILL UNEXPLAINED: lag_sec is a DIFFERENCE OF TWO FILE MTIMES, so it cannot move unless a file 67// moves -- yet untouched gates drifted MORE lag than wall-clock elapsed (nx_aes256_gcm_gate 482948 -> 68// 557268, +20.6h in ~2h) at different rates each. Two independent readers agreed, so an INPUT is moving. 69// THE DECISIVE FIELD IS age_sec = now - mtime. NEGATIVE means the file is stamped in the FUTURE; a large age 70// on a file just written means the writer did not stamp it now (preserve-timestamps). Those two causes are 71// distinguishable ONLY if the stamp and the clock are printed together. 72// * A COMPARISON YOU CANNOT DECOMPOSE INTO ITS OPERANDS IS NOT DEBUGGABLE -- PRINT THE OPERANDS. 73// Nanoseconds shown because the canonical reader compares in ns; a sub-second ordering is invisible in whole 74// seconds, which is the same reason nx_srcfresh works in ns at all. 75func fsc_mtime(path: *u8) -> i64 { 76 if fsx_denied(path) == 1 { 77 fsx_puts("NX-FS-MTIME DENIED: path matches the secret deny-list.\n" as *u8) 78 return 0 - (2 as i64) 79 } 80 let ns: i64 = sf_mtime_ns(path) 81 if ns < 0 { 82 fsx_puts("NX-FS-MTIME ABSENT: cannot stat " as *u8); fsx_puts(path) 83 fsx_puts(" . FIX: confirm the path with `nx_fs ls <dir>`.\n" as *u8) 84 return 0 - 1 85 } 86 let sec: i64 = ns / SF_NS_PER_SEC 87 let now: i64 = sys_now_realtime_sec() 88 fsx_puts("NX-FS-MTIME " as *u8); fsx_puts(path) 89 fsx_puts(" mtime_sec=" as *u8); fsx_putn(sec) 90 fsx_puts(" mtime_ns=" as *u8); fsx_putn(ns - sec * SF_NS_PER_SEC) 91 fsx_puts(" now_sec=" as *u8); fsx_putn(now) 92 fsx_puts(" age_sec=" as *u8) 93 if now >= sec { fsx_putn(now - sec) } 94 else { fsx_puts("-" as *u8); fsx_putn(sec - now); fsx_puts(" FUTURE-STAMPED" as *u8) } 95 fsx_puts(" exact=1\n" as *u8) 96 return sec 97} 98func main(argc: i64, argv: *i64) -> i64 { 99 if argc < FSC_ARGC_P1 { fsx_puts(FSC_USAGE); return FSC_USAGE_RC } 100 let verb: *u8 = argv[FSC_ARG_VERB] as *u8 101 if fsx_seq(verb, "read" as *u8) == 1 { 102 var cap: i64 = 0 103 if argc >= FSC_ARGC_P2 { cap = fsc_atoi(argv[FSC_ARG_P2] as *u8) } 104 var roff: i64 = 0 105 if argc >= 5 { roff = fsc_atoi(argv[4] as *u8) } // read <path> [maxbytes] [offset] -- windowed (seq222) 106 var r: i64 = 0 107 if roff > 0 { r = fsx_read_at(argv[FSC_ARG_P1] as *u8, cap, roff) } else { r = fsx_read(argv[FSC_ARG_P1] as *u8, cap) } 108 if r >= 0 { return 0 } 109 if r == 0 - (2 as i64) { return FSX_RC_DENIED } 110 return FSX_RC_ABSENT 111 } 112 if fsx_seq(verb, "lines" as *u8) == 1 { 113 var lstart: i64 = 1 114 if argc >= FSC_ARGC_P2 { lstart = fsc_atoi(argv[FSC_ARG_P2] as *u8) } 115 var lcount: i64 = 0 116 if argc >= 5 { lcount = fsc_atoi(argv[4] as *u8) } 117 let rl: i64 = fsx_read_lines(argv[FSC_ARG_P1] as *u8, lstart, lcount) 118 if rl >= 0 { return 0 } 119 if rl == 0 - (2 as i64) { return FSX_RC_DENIED } 120 return FSX_RC_ABSENT 121 } 122 if fsx_seq(verb, "tail" as *u8) == 1 { 123 var tcount: i64 = 0 124 if argc >= FSC_ARGC_P2 { tcount = fsc_atoi(argv[FSC_ARG_P2] as *u8) } 125 let rt: i64 = fsx_tail(argv[FSC_ARG_P1] as *u8, tcount) 126 if rt >= 0 { return 0 } 127 if rt == 0 - (2 as i64) { return FSX_RC_DENIED } 128 return FSX_RC_ABSENT 129 } 130 if fsx_seq(verb, "outline" as *u8) == 1 { 131 let r3: i64 = fsx_outline(argv[FSC_ARG_P1] as *u8) 132 if r3 >= 0 { return 0 } 133 if r3 == 0 - (2 as i64) { return FSX_RC_DENIED } 134 return FSX_RC_ABSENT 135 } 136 if fsx_seq(verb, "mtime" as *u8) == 1 { 137 let rm2: i64 = fsc_mtime(argv[FSC_ARG_P1] as *u8) 138 if rm2 >= 0 { return 0 } 139 if rm2 == 0 - (2 as i64) { return FSX_RC_DENIED } 140 return FSX_RC_ABSENT 141 } 142 if fsx_seq(verb, "size" as *u8) == 1 { 143 let rs: i64 = fsx_size(argv[FSC_ARG_P1] as *u8) 144 if rs >= 0 { return 0 } 145 if rs == 0 - (2 as i64) { return FSX_RC_DENIED } 146 return FSX_RC_ABSENT 147 } 148 if fsx_seq(verb, "job" as *u8) == 1 { 149 // THE CLAIM-OR-OUT VERB: the state decides the exit code so a caller branches without a second read 150 let rj: i64 = fsx_job(argv[FSC_ARG_P1] as *u8) 151 if rj == FSX_JOB_RUNNING { return FSX_RC_JOB_RUNNING } 152 if rj == FSX_JOB_REFUSED { return FSC_USAGE_RC } 153 if rj == FSX_JOB_NOSUCH { return FSX_RC_ABSENT } 154 if rj == FSX_JOB_OUT_ABSENT { return FSX_RC_ABSENT } 155 return 0 156 } 157 if fsx_seq(verb, "ls" as *u8) == 1 { 158 // optional 3rd arg = skip, so a directory larger than FSX_LS_CAP is REACHABLE, not merely 159 // honestly refused. Absent => 0 => byte-identical behaviour to every existing caller. 160 var lskip: i64 = 0 161 if argc >= FSC_ARGC_P2 { lskip = fsc_atoi(argv[FSC_ARG_P2] as *u8) } 162 let r2: i64 = fsx_ls_from(argv[FSC_ARG_P1] as *u8, lskip) 163 if r2 >= 0 { return 0 } 164 return FSX_RC_ABSENT 165 } 166 fsx_puts(FSC_USAGE) 167 return FSC_USAGE_RC 168}