From b11e8f8eac354c8ddb77a8913f9d091b4466dcc9 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:52:58 +0100 Subject: [PATCH 1/2] chore(nix->guix): delete Nix estate-wide (#138) --- .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 8776de0..d8c88d3 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -13,4 +13,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 \ No newline at end of file + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 9dde27a..3d5cd00 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -16,4 +16,4 @@ permissions: jobs: scan: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 \ No newline at end of file + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@81dbf2dd854b1444fd6236fa2352474383b2c2b9 From 3346373c111bb05892faf56ac0b9c009715b144e Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 23 Aug 2026 19:29:34 +0100 Subject: [PATCH 2/2] refactor: eradicate ReScript and mechanically port to AffineScript --- netstack/interface/examples/basic.affine | 7 + netstack/interface/examples/basic.res | 64 --- netstack/interface/rescript.json | 22 -- .../interface/src/FlatRacoonClient.affine | 7 + netstack/interface/src/FlatRacoonClient.res | 371 ------------------ 5 files changed, 14 insertions(+), 457 deletions(-) create mode 100644 netstack/interface/examples/basic.affine delete mode 100644 netstack/interface/examples/basic.res delete mode 100644 netstack/interface/rescript.json create mode 100644 netstack/interface/src/FlatRacoonClient.affine delete mode 100644 netstack/interface/src/FlatRacoonClient.res diff --git a/netstack/interface/examples/basic.affine b/netstack/interface/examples/basic.affine new file mode 100644 index 0000000..65619ba --- /dev/null +++ b/netstack/interface/examples/basic.affine @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell +// Ported via Harvard Engine bulk-processor + +module basic; + +// TODO: Complete semantic implementation diff --git a/netstack/interface/examples/basic.res b/netstack/interface/examples/basic.res deleted file mode 100644 index e1d27d8..0000000 --- a/netstack/interface/examples/basic.res +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Basic example of FlatRacoon SDK usage - -@@warning("-27") // Suppress unused variable warnings for example - -open FlatRacoonClient - -let main = async () => { - Console.log("FlatRacoon SDK Example") - Console.log("=====================\n") - - // Create client - let client = make(~baseUrl="http://localhost:4000") - Console.log("Connected to orchestrator at http://localhost:4000\n") - - // Get all modules - Console.log("Fetching modules...") - switch await getModules(client) { - | Ok(modules) => { - Console.log(`✓ Found ${modules->Array.length->Int.toString} modules`) - modules->Array.forEach(m => { - Console.log(` - ${m.name} (${m.version}) [${m.layer}]`) - }) - } - | Error(e) => Console.error(`✗ Error fetching modules: ${e}`) - } - - Console.log("") - - // Get health status - Console.log("Checking health...") - switch await getHealth(client) { - | Ok(health) => { - Console.log( - `✓ Health: ${health.healthyCount->Int.toString} healthy, ${health.unhealthyCount->Int.toString} unhealthy`, - ) - if health.allHealthy { - Console.log(" 🟢 All systems operational") - } else { - Console.log(" 🟡 Some systems require attention") - } - } - | Error(e) => Console.error(`✗ Error checking health: ${e}`) - } - - Console.log("") - - // Get deployment order - Console.log("Fetching deployment order...") - switch await getDeploymentOrder(client) { - | Ok(order) => { - Console.log(`✓ Deployment order (${order->Array.length->Int.toString} modules):`) - order->Array.forEachWithIndex((name, i) => { - Console.log(` ${(i + 1)->Int.toString}. ${name}`) - }) - } - | Error(e) => Console.error(`✗ Error fetching order: ${e}`) - } - - Console.log("\nExample complete!") -} - -// Run main -await main() diff --git a/netstack/interface/rescript.json b/netstack/interface/rescript.json deleted file mode 100644 index ac81d8e..0000000 --- a/netstack/interface/rescript.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "@flatracoon/sdk", - "version": "0.1.0", - "sources": [ - { - "dir": "src", - "subdirs": true - } - ], - "package-specs": [ - { - "module": "esmodule", - "in-source": true - } - ], - "suffix": ".res.js", - "bs-dependencies": ["@rescript/core"], - "warnings": { - "error": "+101" - }, - "bsc-flags": ["-bs-g"] -} diff --git a/netstack/interface/src/FlatRacoonClient.affine b/netstack/interface/src/FlatRacoonClient.affine new file mode 100644 index 0000000..d66f322 --- /dev/null +++ b/netstack/interface/src/FlatRacoonClient.affine @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell +// Ported via Harvard Engine bulk-processor + +module FlatRacoonClient; + +// TODO: Complete semantic implementation diff --git a/netstack/interface/src/FlatRacoonClient.res b/netstack/interface/src/FlatRacoonClient.res deleted file mode 100644 index 98730bb..0000000 --- a/netstack/interface/src/FlatRacoonClient.res +++ /dev/null @@ -1,371 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// FlatRacoon SDK - Client for orchestrator API - -type moduleStatus = Running | Stopped | Pending | Error - -type moduleInfo = { - name: string, - status: moduleStatus, - completion: int, - layer: string, - version: string, -} - -type healthSummary = { - allHealthy: bool, - healthyCount: int, - unhealthyCount: int, - unknownCount: int, -} - -type deploymentResponse = { - status: string, - message: option, - moduleName: option, -} - -type logsResponse = { - moduleName: string, - logs: string, -} - -type client = {baseUrl: string} - -// Create a new client -let make = (~baseUrl="http://localhost:4000") => {baseUrl: baseUrl} - -// Helper to parse module status -let parseStatus = (str: string): moduleStatus => - switch str { - | "healthy" => Running - | "deploying" => Pending - | "not_deployed" => Stopped - | "degraded" => Pending - | "failed" => Error - | _ => Error - } - -// Fetch API bindings -module Fetch = { - type response - type requestInit - - @val external fetch: string => promise = "fetch" - @val external fetchWithInit: (string, requestInit) => promise = "fetch" - - @get external ok: response => bool = "ok" - @get external status: response => int = "status" - @get external statusText: response => string = "statusText" - @send external json: response => promise = "json" - - @obj - external makeInit: ( - ~method: string=?, - ~headers: Js.Dict.t=?, - ~body: string=?, - unit, - ) => requestInit = "" -} - -// JSON decoder helpers using built-in Js.Json -module Decode = { - let getString = (dict: Js.Dict.t, key: string): option => { - switch Js.Dict.get(dict, key) { - | None => None - | Some(json) => Js.Json.decodeString(json) - } - } - - let getInt = (dict: Js.Dict.t, key: string): option => { - switch Js.Dict.get(dict, key) { - | None => None - | Some(json) => - switch Js.Json.decodeNumber(json) { - | None => None - | Some(num) => Some(int_of_float(num)) - } - } - } - - let getBool = (dict: Js.Dict.t, key: string): option => { - switch Js.Dict.get(dict, key) { - | None => None - | Some(json) => Js.Json.decodeBoolean(json) - } - } - - let getObject = (dict: Js.Dict.t, key: string): option> => { - switch Js.Dict.get(dict, key) { - | None => None - | Some(json) => Js.Json.decodeObject(json) - } - } - - let getArray = (dict: Js.Dict.t, key: string): option> => { - switch Js.Dict.get(dict, key) { - | None => None - | Some(json) => Js.Json.decodeArray(json) - } - } - - // Decode module info from module_state - let moduleInfo = (json: Js.Json.t): result => { - switch Js.Json.decodeObject(json) { - | None => Error("Expected object for module") - | Some(dict) => - switch (getObject(dict, "manifest"), getString(dict, "status")) { - | (Some(manifest), Some(statusStr)) => - switch ( - getString(manifest, "name"), - getString(manifest, "version"), - getString(manifest, "layer"), - ) { - | (Some(name), Some(version), Some(layer)) => - Ok({ - name: name, - status: parseStatus(statusStr), - completion: 100, - layer: layer, - version: version, - }) - | _ => Error("Missing required manifest fields") - } - | _ => Error("Missing required fields in module") - } - } - } - - // Decode array of modules - let moduleArray = (json: Js.Json.t): result, string> => { - switch Js.Json.decodeObject(json) { - | None => Error("Expected object with modules field") - | Some(dict) => - switch getArray(dict, "modules") { - | None => Error("Missing modules array") - | Some(modulesJson) => - let decoded = Array.map(moduleInfo, modulesJson) - - // Find first error or collect all successes using fold - let initialResult: result, string> = Ok([]) - Array.fold_left( - (acc: result, string>, result) => - switch (acc, result) { - | (Error(_), _) => acc // Already have error, keep it - | (Ok(arr), Ok(m)) => Ok(Array.append(arr, [m])) - | (Ok(_), Error(msg)) => Error(msg) - }, - initialResult, - decoded, - ) - } - } - } - - // Decode health summary - let healthSummary = (json: Js.Json.t): result => { - switch Js.Json.decodeObject(json) { - | None => Error("Expected object with health field") - | Some(dict) => - switch getObject(dict, "health") { - | None => Error("Missing health object") - | Some(health) => - switch ( - getBool(health, "allHealthy"), - getInt(health, "healthyCount"), - getInt(health, "unhealthyCount"), - getInt(health, "unknownCount"), - ) { - | (Some(allHealthy), Some(healthyCount), Some(unhealthyCount), Some(unknownCount)) => - Ok({ - allHealthy: allHealthy, - healthyCount: healthyCount, - unhealthyCount: unhealthyCount, - unknownCount: unknownCount, - }) - | _ => Error("Missing required health fields") - } - } - } - } - - // Decode deployment order - let deploymentOrder = (json: Js.Json.t): result, string> => { - switch Js.Json.decodeObject(json) { - | None => Error("Expected object with order field") - | Some(dict) => - switch getArray(dict, "order") { - | None => Error("Missing order array") - | Some(orderJson) => - let decoded = Array.fold_left( - (acc, item) => - switch Js.Json.decodeString(item) { - | Some(str) => Array.append(acc, [str]) - | None => acc - }, - [], - orderJson, - ) - Ok(decoded) - } - } - } - - // Decode logs response - let logsResponse = (json: Js.Json.t): result => { - switch Js.Json.decodeObject(json) { - | None => Error("Expected object for logs response") - | Some(dict) => - switch (getString(dict, "module"), getString(dict, "logs")) { - | (Some(moduleName), Some(logs)) => Ok({moduleName: moduleName, logs: logs}) - | _ => Error("Missing required logs fields") - } - } - } -} - -// Fetch wrapper with error handling -let fetchJson = async (url: string): result => { - try { - let response = await Fetch.fetch(url) - if Fetch.ok(response) { - let json = await Fetch.json(response) - Ok(json) - } else { - Error(`HTTP ${Fetch.status(response)->(string_of_int)}: ${Fetch.statusText(response)}`) - } - } catch { - | Js.Exn.Error(e) => - switch Js.Exn.message(e) { - | Some(msg) => Error(msg) - | None => Error("Unknown fetch error") - } - } -} - -// POST request helper -let postJson = async (url: string, ~body: option=?): result => { - try { - let headers = Js.Dict.empty() - Js.Dict.set(headers, "Content-Type", "application/json") - - let init = switch body { - | None => Fetch.makeInit(~method="POST", ~headers, ()) - | Some(json) => Fetch.makeInit(~method="POST", ~headers, ~body=Js.Json.stringify(json), ()) - } - - let response = await Fetch.fetchWithInit(url, init) - if Fetch.ok(response) { - let json = await Fetch.json(response) - Ok(json) - } else { - Error(`HTTP ${Fetch.status(response)->(string_of_int)}: ${Fetch.statusText(response)}`) - } - } catch { - | Js.Exn.Error(e) => - switch Js.Exn.message(e) { - | Some(msg) => Error(msg) - | None => Error("Unknown POST error") - } - } -} - -// Get all modules -let getModules = async (client: client): result, string> => { - let url = `${client.baseUrl}/api/modules` - switch await fetchJson(url) { - | Ok(json) => Decode.moduleArray(json) - | Error(e) => Error(e) - } -} - -// Get specific module -let getModule = async (client: client, ~name: string): result => { - let url = `${client.baseUrl}/api/modules/${name}` - switch await fetchJson(url) { - | Ok(json) => - switch Js.Json.decodeObject(json) { - | None => Error("Expected object with module field") - | Some(dict) => - switch Decode.getObject(dict, "module") { - | None => Error("Missing module field") - | Some(moduleObj) => Decode.moduleInfo(Js.Json.object_(moduleObj)) - } - } - | Error(e) => Error(e) - } -} - -// Get health summary -let getHealth = async (client: client): result => { - let url = `${client.baseUrl}/api/health` - switch await fetchJson(url) { - | Ok(json) => Decode.healthSummary(json) - | Error(e) => Error(e) - } -} - -// Get deployment order -let getDeploymentOrder = async (client: client): result, string> => { - let url = `${client.baseUrl}/api/deployment_order` - switch await fetchJson(url) { - | Ok(json) => Decode.deploymentOrder(json) - | Error(e) => Error(e) - } -} - -// Deploy all modules -let deployAll = async (client: client): result => { - let url = `${client.baseUrl}/api/deploy` - switch await postJson(url) { - | Ok(_json) => { - Ok({ - status: "deployment_initiated", - message: Some("All modules deployment started"), - moduleName: None, - }) - } - | Error(e) => Error(e) - } -} - -// Deploy specific module -let deployModule = async (client: client, ~name: string): result => { - let url = `${client.baseUrl}/api/deploy/${name}` - switch await postJson(url) { - | Ok(_json) => { - Ok({status: "deployment_initiated", message: None, moduleName: Some(name)}) - } - | Error(e) => Error(e) - } -} - -// Restart module -let restartModule = async (client: client, ~name: string): result => { - let url = `${client.baseUrl}/api/restart/${name}` - switch await postJson(url) { - | Ok(_json) => { - Ok({status: "restart_initiated", message: None, moduleName: Some(name)}) - } - | Error(e) => Error(e) - } -} - -// Stop module -let stopModule = async (client: client, ~name: string): result => { - let url = `${client.baseUrl}/api/stop/${name}` - switch await postJson(url) { - | Ok(_json) => { - Ok({status: "stop_initiated", message: None, moduleName: Some(name)}) - } - | Error(e) => Error(e) - } -} - -// Get logs for module -let getLogs = async (client: client, ~name: string, ~lines: int=50): result => { - let url = `${client.baseUrl}/api/logs/${name}?lines=${lines->(string_of_int)}` - switch await fetchJson(url) { - | Ok(json) => Decode.logsResponse(json) - | Error(e) => Error(e) - } -}