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}