# src/npl/nipbox/nipbox.nim # Phase 21: The Teleporter - Networked Object Pipelines import strutils, parseutils, tables, sequtils, json import kdl import libc as lb import editor import term # Phase 26: Visual Cortex # Phase 30: Pledge Constants const PLEDGE_STDIO* = 0x0001'u64 PLEDGE_RPATH* = 0x0002'u64 PLEDGE_WPATH* = 0x0004'u64 PLEDGE_INET* = 0x0008'u64 PLEDGE_EXEC* = 0x0010'u64 PLEDGE_ALL* = 0xFFFFFFFFFFFFFFFF'u64 # Phase 34: Phoenix Version Marker NIPBOX_VERSION* = "v0.8.8-PHOENIX" type PipelineData = seq[Node] # --- ENVIRONMENT --- var env_table = initTable[string, string]() var last_exit_code: int = 0 # --- HELPERS --- var use_logfile = false const SYS_TABLE_ADDR = 0x83000000'u64 type SysTablePrint = object magic: uint32 reserved: uint32 s_rx: pointer s_tx: pointer s_event: pointer s_cmd: pointer s_input: pointer fn_vfs_open: pointer fn_vfs_read: pointer fn_vfs_list: pointer fn_vfs_write: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.} proc print(s: string) = if s.len > 0: let sys = cast[ptr SysTablePrint](SYS_TABLE_ADDR) if sys.fn_vfs_write != nil: discard sys.fn_vfs_write(1, unsafeAddr s[0], uint64(s.len)) proc expand_vars(text: string): string = # Replace $var with env value, including special $? for exit code result = "" var i = 0 while i < text.len: if text[i] == '$': # Extract var name var varname = "" var j = i + 1 if j < text.len and text[j] == '?': varname = "?" j += 1 else: while j < text.len and (text[j].isAlphaNumeric() or text[j] == '_'): varname.add(text[j]) j += 1 if varname.len > 0: if varname == "?": result.add($last_exit_code) elif env_table.hasKey(varname): result.add(env_table[varname]) else: result.add("$" & varname) # Leave unexpanded if not found i = j else: result.add('$') i += 1 else: result.add(text[i]) i += 1 proc render_output(data: PipelineData) = if data.len == 0: return let typeName = if data.len > 0: data[0].name.toUpperAscii() else: "VOID" print("\n\x1b[1;36mTYPE: " & typeName & "\x1b[0m\n") print(repeat("-", 40) & "\n") for node in data: var line = " " for p in node.props: line.add(p.key & ":" & $p.val & " ") for arg in node.args: line.add($arg & " ") # Truncate content for display if too long if line.len > 80: line = line[0..77] & "..." print(line & "\n") print(repeat("-", 40) & "\n") print("Total: " & $data.len & " objects.\n\n") # --- PHASE 30: WORKER SYSTEM --- type WorkerPacket = ref object command_fn: proc(args: seq[string], input: PipelineData): PipelineData args: seq[string] input: PipelineData output: PipelineData exit_code: int pledge_mask: uint64 # Worker trampoline (C-compatible) proc dispatch_worker(arg: uint64) {.cdecl.} = let packet = cast[ptr WorkerPacket](arg) if packet == nil: return # Apply pledge if packet.pledge_mask != PLEDGE_ALL: discard lb.pledge(packet.pledge_mask) # Execute command try: packet.output = packet.command_fn(packet.args, packet.input) packet.exit_code = 0 except: packet.output = @[] packet.exit_code = 1 # Helper to spawn command as worker proc spawn_command(cmd_fn: proc(args: seq[string], input: PipelineData): PipelineData, args: seq[string], input: PipelineData, pledge: uint64): PipelineData = var packet = WorkerPacket( command_fn: cmd_fn, args: args, input: input, output: @[], exit_code: 0, pledge_mask: pledge ) let packet_ptr = cast[uint64](cast[pointer](packet)) let fid = lb.spawn(dispatch_worker, packet_ptr) if fid < 0: # Spawn failed, run inline return cmd_fn(args, input) discard lb.join(fid) return packet.output discard lb.join(fid) return packet.output proc cmd_crash*(args: seq[string], input: PipelineData): PipelineData = print("[NipBox] PREPARING TO CRASH...\n") # Crash Logic: Null Pointer Dereference in Worker let worker_crash = proc(args: seq[string], input: PipelineData): PipelineData = print("[Worker] Goodbye, cruel world!\n") var ptr_null = cast[ptr int](0) ptr_null[] = 42 # PAGE FAULT return @[] # Spawn the suicider return spawn_command(worker_crash, args, input, PLEDGE_ALL) # Spawn the suicider return spawn_command(worker_crash, args, input, PLEDGE_ALL) proc cmd_upgrade*(args: seq[string], input: PipelineData): PipelineData = if args.len < 1: print("Usage: sys.upgrade \n") return @[] let path = args[0] print("[NipBox] Initiating Phoenix Protocol for Self...\n") print("[NipBox] Target: " & path & "\n") # Upgrade Self (Subject runs as ID 3 usually) let res = lb.upgrade(3, path.cstring) if res < 0: print("Error: Upgrade failed (" & $res & ")\n") # Does not return if success. return @[] # --- COMMANDS --- proc cmd_ls*(args: seq[string], input: PipelineData): PipelineData = result = @[] let files = lb.get_vfs_listing() for f in files: let node = newNode("file") node.addArg(newVal(f)) node.addProp("name", newVal(f)) if f.endsWith(".nsh"): node.addProp("type", newVal("script")) node.addProp("size", newVal(335)) elif f.contains("nipbox"): node.addProp("type", newVal("binary")) node.addProp("size", newVal(800000)) else: node.addProp("type", newVal("unknown")) node.addProp("size", newVal(100)) result.add(node) proc cmd_mount*(args: seq[string], input: PipelineData): PipelineData = print("[mount] System Disk Engaged.\n") return @[] proc cmd_matrix*(args: seq[string], input: PipelineData): PipelineData = let state = if args.len > 0: args[0].toUpperAscii() else: "STATUS: NOMINAL" print("[matrix] " & state & "\n") return @[] proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData = if args.len == 0: return @[] let fd = lb.open(args[0].cstring, 0) if fd < 0: print("Error: Could not open " & args[0] & "\n") return @[] var buf: array[1024, char] while true: let n = lb.read(fd, addr buf[0], 1024) if n <= 0: break discard lb.write(cint(1), addr buf[0], csize_t(n)) discard lb.close(fd) print("\n") return @[] proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData = if args.len == 0: print("Usage: edit \n") return @[] start_editor(args[0]) return @[] proc cmd_echo*(args: seq[string], input: PipelineData): PipelineData = let msg = args.join(" ") if input.len == 0: print(msg & "\n") let node = newNode("text") node.addArg(newVal(msg)) node.addProp("content", newVal(msg)) return @[node] proc cmd_where*(args: seq[string], input: PipelineData): PipelineData = if args.len < 3: print("Usage: where \n") return input let key = args[0] let op = args[1] let targetValStr = args[2] var targetVal = 0 var isInt = parseInt(targetValStr, targetVal) > 0 result = @[] for node in input: var found = false var nodeValInt = 0 var nodeValStr = "" for p in node.props: if p.key == key: if p.val.kind == VInt: nodeValInt = p.val.i found = true elif p.val.kind == VString: nodeValStr = p.val.s discard parseInt(nodeValStr, nodeValInt) found = true break if found: let match = case op: of ">": nodeValInt > targetVal of "<": nodeValInt < targetVal of "==": (if not isInt: nodeValStr == targetValStr else: nodeValInt == targetVal) else: false if match: result.add(node) # --- PHASE 21: THE TELEPORTER --- proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData = if args.len == 0: print("Usage: http.get \n") return @[] let target = args[0] let parts = target.split(':') if parts.len != 2: print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n") return @[] let ip_str = parts[0] let port = uint16(parseInt(parts[1])) # Parse IP (A.B.C.D) let ip_parts = ip_str.split('.') if ip_parts.len != 4: return @[] # LwIP IP encoding (Network Byte Order for internal, but our pack uses uint32) # Actually net_glue.nim uses the same pack logic. let ip_val = (uint32(parseInt(ip_parts[0])) shl 0) or (uint32(parseInt(ip_parts[1])) shl 8) or (uint32(parseInt(ip_parts[2])) shl 16) or (uint32(parseInt(ip_parts[3])) shl 24) print("[Teleporter] Connecting to " & target & "...\n") let fd = lb.socket(2, 1, 0) # AF_INET=2, SOCK_STREAM=1 if fd < 100: return @[] # Construct SockAddrIn type SockAddrIn = object sin_family: uint16 sin_port: uint16 sin_addr: uint32 sin_zero: array[8, char] var addr_in: SockAddrIn addr_in.sin_family = 2 # htons for port (8000 -> 0x401F -> 0x1F40? No, manual) addr_in.sin_port = ((port and 0xFF) shl 8) or (port shr 8) addr_in.sin_addr = ip_val if lb.connect(fd, addr addr_in, sizeof(addr_in)) < 0: print("Error: Handshake FAILED.\n") return @[] # Wait for establishment (pumping the stack) var timeout = 0 while timeout < 1000: lb.pump_membrane_stack() # Check if connected (we need a way to check socket state) # For now, let's assume if we can send, we are connected or it will buffer. # In our net_glue, glue_write returns -1 if not established. let test_req = "GET / HTTP/1.1\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n" let n = lb.send(cint(fd), cast[pointer](unsafeAddr test_req[0]), csize_t( test_req.len), 0) if n > 0: break timeout += 1 # Busy wait a bit for i in 0..1000: discard if timeout >= 1000: print("Error: Connection TIMEOUT.\n") discard lb.close(cint(fd)) return @[] print("[Teleporter] Request Sent. Waiting for response...\n") var response_body = "" var buf: array[2048, char] timeout = 0 while timeout < 5000: lb.pump_membrane_stack() let n = lb.recv(cint(fd), addr buf[0], 2048, 0) if n > 0: for i in 0.. \n") return @[] let url_arg = args[0] let outfile = args[1] var host_part = url_arg var path_str = "/" let slash_pos = url_arg.find('/') if slash_pos != -1: host_part = url_arg[0.. 0: timeout = 0 if not header_parsed: for i in 0.. 8192: print("Error: Headers too large.\n") break else: discard lb.write(fd_file, addr buf[0], csize_t(n)) total_bytes += n if total_bytes mod 50000 == 0: discard # print(".") elif n == 0: break else: timeout += 1 for i in 0..1000: discard discard lb.close(fd_file) discard lb.close(cint(fd)) print("\n[Download] Complete. " & $total_bytes & " bytes.\n") return @[] proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData = if input.len == 0: return @[] result = @[] for inNode in input: var body = "" for p in inNode.props: if p.key == "body": body = p.val.s break if body == "": continue try: # Find start of JSON if header is present let start = body.find('{') if start == -1: continue let json_str = body[start..^1] let j = parseJson(json_str) if j.kind == JObject: let outNode = newNode("data") for k, v in j.fields: case v.kind: of JString: outNode.addProp(k, newVal(v.getStr())) of JInt: outNode.addProp(k, newVal(int(v.getBiggestInt()))) of JFloat: outNode.addProp(k, newVal($v.getFloat())) # KDL value doesn't support float yet? of JBool: outNode.addProp(k, newVal(if v.getBool(): 1 else: 0)) else: discard result.add(outNode) elif j.kind == JArray: for item in j: if item.kind == JObject: let outNode = newNode("data") for k, v in item.fields: case v.kind: of JString: outNode.addProp(k, newVal(v.getStr())) of JInt: outNode.addProp(k, newVal(int(v.getBiggestInt()))) else: discard result.add(outNode) except: print("Error: JSON Parse failed.\n") proc cmd_set*(args: seq[string], input: PipelineData): PipelineData = # Syntax: set key = value if args.len < 3 or args[1] != "=": print("Usage: set = \n") last_exit_code = 1 return @[] let key = args[0] let value = args[2..^1].join(" ") env_table[key] = value last_exit_code = 0 return @[] proc cmd_help*(args: seq[string], input: PipelineData): PipelineData = print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n") print("Commands: ls, cat, echo, where, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n") return @[] # --- DISPATCHER --- proc dispatch_command(name: string, args: seq[string], input: PipelineData): PipelineData = let cmd = name.toLowerAscii().strip() if cmd.len == 0: return input case cmd: of "ls": return cmd_ls(args, input) of "cat": return cmd_cat(args, input) of "edit": return cmd_edit(args, input) of "echo": return cmd_echo(args, input) of "where": return cmd_where(args, input) of "http.get": # Phase 30: Spawn in worker with INET pledge only (no file access) return spawn_command(cmd_http_get, args, input, PLEDGE_INET or PLEDGE_STDIO) of "http.download": # Phase 34: Spawn in worker with INET and R/W PATH pledge (needs to write file) # PLEDGE_WPATH (0x4) + PLEDGE_INET (0x8) + PLEDGE_STDIO (0x1) = 0xD return spawn_command(cmd_http_download, args, input, PLEDGE_INET or PLEDGE_WPATH or PLEDGE_STDIO) of "from_json": return cmd_from_json(args, input) of "mount": return cmd_mount(args, input) of "matrix": return cmd_matrix(args, input) of "crash": return cmd_crash(args, input) of "sys.upgrade": return cmd_upgrade(args, input) of "set": return cmd_set(args, input) of "help": return cmd_help(args, input) of "exit": lb.exit(0) return @[] else: print("Error: Command '" & cmd & "' not recognized.\n") last_exit_code = 127 return @[] # Forward declaration for recursive calls proc process_pipeline*(line: string) proc execute_block(lines: seq[string]) proc parse_block(text: string, startIdx: int): tuple[content: string, endIdx: int] = # Find matching closing brace var depth = 0 var i = startIdx var blockContent = "" var started = false while i < text.len: if text[i] == '{': if started: blockContent.add('{') depth += 1 else: started = true i += 1 elif text[i] == '}': if depth > 0: blockContent.add('}') depth -= 1 i += 1 else: return (blockContent, i) else: if started: blockContent.add(text[i]) i += 1 return (blockContent, i) proc eval_condition(condLine: string): bool = # Execute the condition as a pipeline and check exit code last_exit_code = 0 process_pipeline(condLine) return last_exit_code == 0 proc execute_block(lines: seq[string]) = for line in lines: process_pipeline(line) proc cmd_if*(fullLine: string) = # Parse: if { } let parts = fullLine.strip().splitWhitespace(maxsplit = 1) if parts.len < 2: print("Usage: if { ... }\n") last_exit_code = 1 return let restLine = parts[1] let bracePos = restLine.find('{') if bracePos == -1: print("Error: if block missing '{'\n") last_exit_code = 1 return let condition = restLine[0.. 0) execute_block(blockLines) last_exit_code = 0 proc cmd_while*(fullLine: string) = # Parse: while { } let parts = fullLine.strip().splitWhitespace(maxsplit = 1) if parts.len < 2: print("Usage: while { ... }\n") last_exit_code = 1 return let restLine = parts[1] let bracePos = restLine.find('{') if bracePos == -1: print("Error: while block missing '{'\n") last_exit_code = 1 return let condition = restLine[0.. 0) while eval_condition(condition): execute_block(blockLines) last_exit_code = 0 proc process_pipeline*(line: string) = let expandedLine = expand_vars(line) let cleanLine = expandedLine.strip() if cleanLine.len == 0 or cleanLine.startsWith("#"): return # Check for control flow if cleanLine.startsWith("if "): cmd_if(cleanLine) return elif cleanLine.startsWith("while "): cmd_while(cleanLine) return var redirectionFile = "" var pipelineText = cleanLine # Find redirection at the end of the line let lastGt = cleanLine.rfind('>') if lastGt != -1: # Check if this > is likely a redirection (preceded by space or end of command) # Simple heuristic: if it's the last segment and followed by a "path-like" string let potentialFile = cleanLine[lastGt+1..^1].strip() if potentialFile.len > 0 and not potentialFile.contains(' '): # Most likely a redirection pipelineText = cleanLine[0.. 1: parts[1..^1] else: @[] current_blood = dispatch_command(cmdName, args, current_blood) # Exit code: success if we got data, failure if empty (unless piped) if current_blood.len == 0: if segIdx < segments.len - 1: break else: last_exit_code = 1 if current_blood.len > 0: if redirectionFile.len > 0: # Write to file (Sovereign Write) var content = "" for node in current_blood: content.add(node.render()) let fd = lb.open(redirectionFile.cstring, 577) # O_WRONLY | O_CREAT | O_TRUNC if fd >= 0: discard lb.write(fd, cast[pointer](unsafeAddr content[0]), csize_t(content.len)) discard lb.close(fd) print("[VFS] Data diverted to: " & redirectionFile & "\n") else: print("[VFS] Error: Could not open '" & redirectionFile & "' for diversion.\n") else: render_output(current_blood) # --- BOOTSTRAP --- proc run_script(path: string) = let fd = lb.open(path.cstring, 0) if fd < 0: return var buf = newString(8192) let n = lb.read(fd, addr buf[0], 8192) if n > 0: buf.setLen(n) discard lb.close(fd) if n > 0: var currentLine = "" for c in buf: if c == '\n' or c == '\r': if currentLine.strip().len > 0: process_pipeline(currentLine) currentLine = "" else: currentLine.add(c) if currentLine.strip().len > 0: process_pipeline(currentLine) # --- MAIN --- proc main() = # Initialize the Biosuit print("[NipBox] Booting...\n") lb.membrane_init() # term.term_init() # Phase 26: Visual Cortex Init - DISABLED print("\n\x1b[1;32m╔═══════════════════════════════════════╗\x1b[0m\n") print("\x1b[1;32m║ SOVEREIGN SUPERVISOR v0.8.7 ║\x1b[0m\n") print("\x1b[1;32m║ PHASE 21: THE TELEPORTER ACTIVATED ║\x1b[0m\n") print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n") # run_script("/etc/init.nsh") print("\x1b[1;33mroot@nexus:# \x1b[0m") var inputBuffer: string = "" var loop_counter: uint64 = 0 print("[NipBox] Entering main REPL loop...\n") print("\x1b[1;33mroot@nexus:# \x1b[0m") # INITIAL PROMPT while true: loop_counter += 1 lb.pump_membrane_stack() var c: char let n = lb.read(0, addr c, 1) if n > 0: if c == '\n' or c == '\r': print("\n") process_pipeline(inputBuffer) inputBuffer = "" print("\x1b[1;33mroot@nexus:# \x1b[0m") elif c == '\b' or c == char(127): if inputBuffer.len > 0: print("\b \b") inputBuffer.setLen(inputBuffer.len - 1) else: inputBuffer.add(c) var s = "" s.add(c) print(s) else: # Slow down polling for i in 0..10_000: discard when isMainModule: main()