code wiki / (root) / nx_fsops_t77.nx

nx_fsops_t77.nx source

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