| … | … | @@ -0,0 +1,4 @@ |
| 1 | node_modules/ | |
| 2 | m0_roundtrip | |
| 3 | *.o | |
| 4 | *.sock |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | # M0 — UDS bridge + codec round-trip | |
| 2 | ||
| 3 | First milestone of the HyperStorageNode (spec: `../../spec/hyperstoragenode.md`; | |
| 4 | `atomspace` org issue #4). Proves the data path with **no Hypercore** and **no full | |
| 5 | StorageNode/guile-module boilerplate** yet: | |
| 6 | ||
| 7 | ``` | |
| 8 | C++ (real Sexpr codec) Bare daemon (in-memory s-expr log) | |
| 9 | ──────────────────────── ───────────────────────────────── | |
| 10 | build small AtomSpace | |
| 11 | Sexpr::encode_atom ──store──▶ log.push(sexpr) | |
| 12 | (barrier) ──barrier─▶ ok | |
| 13 | (replay) ──load────▶ (sexpr1 sexpr2 …) | |
| 14 | Sexpr::decode_atom ◀─────────── | |
| 15 | gold check: src AtomSpace ≡ dst AtomSpace → M0 GOLD PASS | |
| 16 | ``` | |
| 17 | ||
| 18 | ## What this establishes | |
| 19 | - A C++ `AF_UNIX` client (`HyperChannel`, adapted from `CogSimpleStorage`'s | |
| 20 | `do_send`/`do_recv`, TCP→UDS) round-trips atoms through a **Bare** daemon. | |
| 21 | - The wire payload is the **real opencog s-expr codec** (`Sexpr::encode_atom` / | |
| 22 | `Sexpr::decode_atom` from `libsexpr`), not a bespoke serialization — so encode is | |
| 23 | a genuine inverse of decode. | |
| 24 | - bare-net UDS framing + half-open handling (`allowHalfOpen:false` + destroy on | |
| 25 | `end`, the hyper-svc leak fix) work against a foreign (C++) client. | |
| 26 | ||
| 27 | ## Files | |
| 28 | | File | Role | | |
| 29 | |---|---| | |
| 30 | | `daemon.js` | Bare UDS server; in-memory `log[]` of s-expr strings; verbs `store`/`load`/`clear`/`barrier`/`count`. At M1 the array becomes a Hypercore. | | |
| 31 | | `m0_roundtrip.cc` | C++ harness. `HyperChannel` = the reusable transport the future `HyperStorageNode` embeds; `main()` builds an 8-atom AtomSpace, stores, replays, gold-checks. | | |
| 32 | | `run.sh` | build + start daemon + run harness + gold check + cleanup. | | |
| 33 | ||
| 34 | ## Run | |
| 35 | ```bash | |
| 36 | ./run.sh # prints "M0 GOLD PASS", exits 0 | |
| 37 | ``` | |
| 38 | Requires: `bare` (v1.26+), `g++` (C++20), opencog installed under `/usr/local` | |
| 39 | (`libatomspace`, `libsexpr`, `libatombase`, `libatom_types`). `npm install` pulls | |
| 40 | `bare-net`/`bare-fs`/`bare-process` into a local `node_modules` (gitignored). | |
| 41 | ||
| 42 | ## Wire protocol (newline-framed) | |
| 43 | | Request | Reply | Meaning | | |
| 44 | |---|---|---| | |
| 45 | | `store <sexpr>` | — | append one atom's s-expr to the log (fire-and-forget) | | |
| 46 | | `load` | `(<sexpr>…)\n` | whole log wrapped in one outer paren (the shape the C++ decode loop expects) | | |
| 47 | | `clear` | `ok\n` | empty the log | | |
| 48 | | `barrier` | `ok\n` | sync point (ordered stream ⇒ all prior stores are applied) | | |
| 49 | | `count` | `<n>\n` | log length (debug) | | |
| 50 | ||
| 51 | ## Deliberately NOT here (deferred) | |
| 52 | - **Hypercore** — the log is a plain array (that is M1: append + replay + fork). | |
| 53 | - **Values** — structure only; per the synthesis rule *event-source the graph, | |
| 54 | checkpoint the Values* (`../../spec/activegraph-synthesis.md`). | |
| 55 | - **Full `HyperStorageNode : public StorageNode` + guile module** — `HyperChannel` | |
| 56 | is written to lift straight into it at M1; no atom_types/PersistSCM/CMake yet. | |
| 57 | - **Autobase multi-writer** — single client, single daemon (M4). |
| … | … | @@ -0,0 +1,95 @@ |
| 1 | // M0 hyper-atomspace daemon (Bare). | |
| 2 | // | |
| 3 | // Purpose: prove the HyperStorageNode bridge + s-expr codec round-trip with an | |
| 4 | // in-memory op-log. NO Hypercore yet (that is M1). The daemon holds an array of | |
| 5 | // s-expr strings (one per stored atom) and can replay them on demand. | |
| 6 | // | |
| 7 | // Wire protocol — newline-framed request lines over a UNIX-domain socket: | |
| 8 | // store <sexpr>\n append <sexpr> to the log. NO reply (fire-and-forget, | |
| 9 | // mirrors CogSimpleStorage::storeAtom). | |
| 10 | // load\n reply one line: (<sexpr1><sexpr2>...)\n — the whole log | |
| 11 | // wrapped in a single outer paren, the exact shape the C++ | |
| 12 | // side's decode loop (cf. CogSimpleStorage::decode_atom_list) | |
| 13 | // expects. | |
| 14 | // clear\n empty the log. reply: ok\n | |
| 15 | // barrier\n sync point. reply: ok\n | |
| 16 | // count\n reply: <n>\n (debug) | |
| 17 | // | |
| 18 | // Framing note: bare-net defaults allowHalfOpen:true and only emits 'close' on an | |
| 19 | // explicit destroy (see hyper-svc UDS leak post-mortem). We create the server | |
| 20 | // allowHalfOpen:false AND destroy on 'end' so client fds are released. | |
| 21 | ||
| 22 | import net from 'bare-net' | |
| 23 | import fs from 'bare-fs' | |
| 24 | import process from 'bare-process' | |
| 25 | ||
| 26 | const sockPath = process.argv[2] | |
| 27 | if (!sockPath) { | |
| 28 | console.error('usage: bare daemon.js <socket-path>') | |
| 29 | process.exit(1) | |
| 30 | } | |
| 31 | ||
| 32 | // Fresh bind: a stale socket file makes listen() fail with EADDRINUSE. | |
| 33 | try { fs.unlinkSync(sockPath) } catch (_) { /* not there — fine */ } | |
| 34 | ||
| 35 | /** @type {string[]} the in-memory op-log: one s-expr per stored atom. */ | |
| 36 | const log = [] | |
| 37 | ||
| 38 | function handleLine (line, sock) { | |
| 39 | if (line.length === 0) return | |
| 40 | const sp = line.indexOf(' ') | |
| 41 | const verb = sp === -1 ? line : line.slice(0, sp) | |
| 42 | const rest = sp === -1 ? '' : line.slice(sp + 1) | |
| 43 | ||
| 44 | switch (verb) { | |
| 45 | case 'store': | |
| 46 | log.push(rest) | |
| 47 | return // no reply | |
| 48 | case 'load': | |
| 49 | sock.write('(' + log.join('') + ')\n') | |
| 50 | return | |
| 51 | case 'clear': | |
| 52 | log.length = 0 | |
| 53 | sock.write('ok\n') | |
| 54 | return | |
| 55 | case 'barrier': | |
| 56 | sock.write('ok\n') | |
| 57 | return | |
| 58 | case 'count': | |
| 59 | sock.write(String(log.length) + '\n') | |
| 60 | return | |
| 61 | default: | |
| 62 | sock.write('err unknown-verb ' + verb + '\n') | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | const server = net.createServer({ allowHalfOpen: false }, (sock) => { | |
| 67 | let buf = '' | |
| 68 | sock.on('data', (chunk) => { | |
| 69 | buf += chunk.toString() | |
| 70 | let nl | |
| 71 | while ((nl = buf.indexOf('\n')) !== -1) { | |
| 72 | const line = buf.slice(0, nl) | |
| 73 | buf = buf.slice(nl + 1) | |
| 74 | handleLine(line, sock) | |
| 75 | } | |
| 76 | }) | |
| 77 | // Peer finished + disconnected. bare-net will not emit 'close' on its own; | |
| 78 | // destroy to release the fd (the hyper-svc leak fix). | |
| 79 | sock.on('end', () => sock.destroy()) | |
| 80 | sock.on('error', () => sock.destroy()) | |
| 81 | }) | |
| 82 | ||
| 83 | server.listen(sockPath, () => { | |
| 84 | // Single line on stdout = readiness signal for run.sh / test harness. | |
| 85 | console.log('hyper-atomspace-m0 listening ' + sockPath) | |
| 86 | }) | |
| 87 | ||
| 88 | // Clean shutdown so the socket file does not linger. | |
| 89 | function shutdown () { | |
| 90 | try { server.close() } catch (_) {} | |
| 91 | try { fs.unlinkSync(sockPath) } catch (_) {} | |
| 92 | process.exit(0) | |
| 93 | } | |
| 94 | process.on('SIGINT', shutdown) | |
| 95 | process.on('SIGTERM', shutdown) |
| … | … | @@ -0,0 +1,191 @@ |
| 1 | /* | |
| 2 | * m0_roundtrip.cc — M0 bridge + codec round-trip harness. | |
| 3 | * | |
| 4 | * Proves the HyperStorageNode data path with NO Hypercore and NO full | |
| 5 | * StorageNode/guile-module boilerplate yet: build a small AtomSpace, encode each | |
| 6 | * atom with the REAL opencog s-expr codec (Sexpr::encode_atom), ship it over a | |
| 7 | * UNIX-domain socket to the Bare `hyper-atomspace` daemon (daemon.js), replay the | |
| 8 | * log back, decode with the REAL codec (Sexpr::decode_atom) into a fresh | |
| 9 | * AtomSpace, and gold-check that the two AtomSpaces are identical. | |
| 10 | * | |
| 11 | * `HyperChannel` is the reusable AF_UNIX transport (adapted from | |
| 12 | * CogSimpleStorage's do_send/do_recv, TCP -> UDS). At M1 it lifts straight into | |
| 13 | * `HyperStorageNode : public StorageNode` (cf. CogChannel in cog-storage); here | |
| 14 | * main() is just the test harness around it. | |
| 15 | * | |
| 16 | * SPDX-License-Identifier: AGPL-3.0-or-later | |
| 17 | */ | |
| 18 | ||
| 19 | #include <algorithm> | |
| 20 | #include <iostream> | |
| 21 | #include <string> | |
| 22 | #include <vector> | |
| 23 | ||
| 24 | #include <string.h> | |
| 25 | #include <sys/socket.h> | |
| 26 | #include <sys/un.h> | |
| 27 | #include <unistd.h> | |
| 28 | ||
| 29 | #include <opencog/atomspace/AtomSpace.h> | |
| 30 | #include <opencog/atoms/atom_types/atom_types.h> | |
| 31 | #include <opencog/atoms/base/Handle.h> | |
| 32 | #include <opencog/persist/sexpr/Sexpr.h> | |
| 33 | ||
| 34 | using namespace opencog; | |
| 35 | ||
| 36 | // --------------------------------------------------------------------------- | |
| 37 | // HyperChannel — newline-framed s-expr protocol over a UNIX-domain socket. | |
| 38 | // (The transport the future HyperStorageNode will embed.) | |
| 39 | class HyperChannel | |
| 40 | { | |
| 41 | int _fd = -1; | |
| 42 | std::string _rbuf; // leftover bytes past a newline | |
| 43 | ||
| 44 | public: | |
| 45 | void open(const std::string& path) | |
| 46 | { | |
| 47 | _fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
| 48 | if (_fd < 0) throw std::runtime_error("socket() failed"); | |
| 49 | ||
| 50 | struct sockaddr_un addr; | |
| 51 | memset(&addr, 0, sizeof(addr)); | |
| 52 | addr.sun_family = AF_UNIX; | |
| 53 | if (path.size() >= sizeof(addr.sun_path)) | |
| 54 | throw std::runtime_error("socket path too long"); | |
| 55 | strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); | |
| 56 | ||
| 57 | if (connect(_fd, (struct sockaddr*) &addr, sizeof(addr)) != 0) | |
| 58 | throw std::runtime_error(std::string("connect() failed: ") | |
| 59 | + strerror(errno)); | |
| 60 | } | |
| 61 | ||
| 62 | void close_fd() | |
| 63 | { | |
| 64 | if (_fd >= 0) { ::close(_fd); _fd = -1; } | |
| 65 | } | |
| 66 | ~HyperChannel() { close_fd(); } | |
| 67 | ||
| 68 | // Fire-and-forget send of one line (must include its own '\n'). | |
| 69 | void send_line(const std::string& line) | |
| 70 | { | |
| 71 | size_t off = 0; | |
| 72 | while (off < line.size()) | |
| 73 | { | |
| 74 | ssize_t n = ::send(_fd, line.data() + off, line.size() - off, 0); | |
| 75 | if (n < 0) throw std::runtime_error("send() failed"); | |
| 76 | off += n; | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | // Read exactly one '\n'-terminated line; returns it without the newline. | |
| 81 | std::string recv_line() | |
| 82 | { | |
| 83 | for (;;) | |
| 84 | { | |
| 85 | size_t nl = _rbuf.find('\n'); | |
| 86 | if (nl != std::string::npos) | |
| 87 | { | |
| 88 | std::string line = _rbuf.substr(0, nl); | |
| 89 | _rbuf.erase(0, nl + 1); | |
| 90 | return line; | |
| 91 | } | |
| 92 | char buf[8192]; | |
| 93 | ssize_t n = ::recv(_fd, buf, sizeof(buf), 0); | |
| 94 | if (n < 0) throw std::runtime_error("recv() failed"); | |
| 95 | if (n == 0) throw std::runtime_error("daemon closed connection"); | |
| 96 | _rbuf.append(buf, n); | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | // Protocol verbs | |
| 101 | void store(const std::string& sexpr) { send_line("store " + sexpr + "\n"); } | |
| 102 | void clear() { send_line("clear\n"); (void) recv_line(); } | |
| 103 | void barrier() { send_line("barrier\n"); (void) recv_line(); } | |
| 104 | std::string load() { send_line("load\n"); return recv_line(); } | |
| 105 | }; | |
| 106 | ||
| 107 | // --------------------------------------------------------------------------- | |
| 108 | // Enumerate every atom in `as`, encode each to its s-expr, return sorted vector. | |
| 109 | static std::vector<std::string> encoded_sorted(const AtomSpacePtr& as) | |
| 110 | { | |
| 111 | HandleSeq all; | |
| 112 | as->get_handles_by_type(all, ATOM, true /* subclasses */); | |
| 113 | std::vector<std::string> out; | |
| 114 | for (const Handle& h : all) | |
| 115 | out.push_back(Sexpr::encode_atom(h)); | |
| 116 | std::sort(out.begin(), out.end()); | |
| 117 | return out; | |
| 118 | } | |
| 119 | ||
| 120 | int main(int argc, char** argv) | |
| 121 | { | |
| 122 | if (argc < 2) | |
| 123 | { | |
| 124 | std::cerr << "usage: m0_roundtrip <socket-path>\n"; | |
| 125 | return 2; | |
| 126 | } | |
| 127 | const std::string sockpath = argv[1]; | |
| 128 | ||
| 129 | // --- 1. Build a source AtomSpace: nodes, a link, a nested Eval/List. --- | |
| 130 | AtomSpacePtr src = createAtomSpace(); | |
| 131 | Handle cat = src->add_node(CONCEPT_NODE, "cat"); | |
| 132 | Handle animal = src->add_node(CONCEPT_NODE, "animal"); | |
| 133 | src->add_link(INHERITANCE_LINK, cat, animal); | |
| 134 | ||
| 135 | Handle whale = src->add_node(CONCEPT_NODE, "whale"); | |
| 136 | Handle water = src->add_node(CONCEPT_NODE, "water"); | |
| 137 | Handle livesIn = src->add_node(PREDICATE_NODE, "lives-in"); | |
| 138 | Handle lst = src->add_link(LIST_LINK, whale, water); | |
| 139 | src->add_link(EVALUATION_LINK, livesIn, lst); | |
| 140 | ||
| 141 | // --- 2. Store every atom over the UDS bridge. --- | |
| 142 | HyperChannel ch; | |
| 143 | ch.open(sockpath); | |
| 144 | ch.clear(); | |
| 145 | for (const std::string& s : encoded_sorted(src)) | |
| 146 | ch.store(s); | |
| 147 | ch.barrier(); | |
| 148 | ||
| 149 | // --- 3. Replay the log; decode into a fresh AtomSpace. --- | |
| 150 | std::string reply = ch.load(); | |
| 151 | AtomSpacePtr dst = createAtomSpace(); | |
| 152 | { | |
| 153 | std::unordered_map<std::string, Handle> cache; | |
| 154 | size_t l = reply.find('('); | |
| 155 | size_t end = reply.rfind(')'); | |
| 156 | if (l != std::string::npos && end != std::string::npos && l < end) | |
| 157 | { | |
| 158 | l += 1; // step past the outer '(' | |
| 159 | size_t r = end; | |
| 160 | while (true) | |
| 161 | { | |
| 162 | int pcnt = Sexpr::get_next_expr(reply, l, r, 0); | |
| 163 | if (l == r) break; | |
| 164 | if (0 < pcnt) break; // unbalanced — malformed reply | |
| 165 | Handle h = Sexpr::decode_atom(reply, l, r, 0, cache); | |
| 166 | dst->add_atom(h); | |
| 167 | l = r + 1; | |
| 168 | r = end; | |
| 169 | } | |
| 170 | } | |
| 171 | } | |
| 172 | ch.close_fd(); | |
| 173 | ||
| 174 | // --- 4. Gold check: src and dst must be byte-identical atom sets. --- | |
| 175 | std::vector<std::string> es = encoded_sorted(src); | |
| 176 | std::vector<std::string> ed = encoded_sorted(dst); | |
| 177 | ||
| 178 | std::cout << "src atoms: " << src->get_size() | |
| 179 | << " dst atoms: " << dst->get_size() << "\n"; | |
| 180 | ||
| 181 | bool ok = (es == ed) && (src->get_size() == dst->get_size()); | |
| 182 | if (not ok) | |
| 183 | { | |
| 184 | std::cout << "MISMATCH\n src:\n"; | |
| 185 | for (const auto& s : es) std::cout << " " << s << "\n"; | |
| 186 | std::cout << " dst:\n"; | |
| 187 | for (const auto& s : ed) std::cout << " " << s << "\n"; | |
| 188 | } | |
| 189 | std::cout << (ok ? "M0 GOLD PASS" : "M0 GOLD FAIL") << "\n"; | |
| 190 | return ok ? 0 : 1; | |
| 191 | } |
| … | … | @@ -0,0 +1,305 @@ |
| 1 | { | |
| 2 | "name": "hyper-atomspace-m0", | |
| 3 | "version": "0.0.0", | |
| 4 | "lockfileVersion": 3, | |
| 5 | "requires": true, | |
| 6 | "packages": { | |
| 7 | "": { | |
| 8 | "name": "hyper-atomspace-m0", | |
| 9 | "version": "0.0.0", | |
| 10 | "dependencies": { | |
| 11 | "bare-fs": "^4.7.3", | |
| 12 | "bare-net": "^2.3.2", | |
| 13 | "bare-process": "^4.5.0" | |
| 14 | } | |
| 15 | }, | |
| 16 | "node_modules/b4a": { | |
| 17 | "version": "1.8.1", | |
| 18 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", | |
| 19 | "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", | |
| 20 | "license": "Apache-2.0", | |
| 21 | "peerDependencies": { | |
| 22 | "react-native-b4a": "*" | |
| 23 | }, | |
| 24 | "peerDependenciesMeta": { | |
| 25 | "react-native-b4a": { | |
| 26 | "optional": true | |
| 27 | } | |
| 28 | } | |
| 29 | }, | |
| 30 | "node_modules/bare-abort": { | |
| 31 | "version": "2.0.13", | |
| 32 | "resolved": "https://registry.npmjs.org/bare-abort/-/bare-abort-2.0.13.tgz", | |
| 33 | "integrity": "sha512-zdc8l88eB11Jsz5rDd6sCAgv2kUFXgdrZWoMlgU6JMkfAi1/uuGFC3IEHswKbIRQTk5H3T5CMuechsXYxiaHlQ==", | |
| 34 | "license": "Apache-2.0" | |
| 35 | }, | |
| 36 | "node_modules/bare-dns": { | |
| 37 | "version": "2.1.4", | |
| 38 | "resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz", | |
| 39 | "integrity": "sha512-abwjHmpWqSRNB7V5615QxPH92L71AVzFm/kKTs8VYiNTAi2xVdonpv0BjJ0hwXLwomoW+xsSOPjW6PZPO14asg==", | |
| 40 | "license": "Apache-2.0", | |
| 41 | "engines": { | |
| 42 | "bare": ">=1.7.0" | |
| 43 | } | |
| 44 | }, | |
| 45 | "node_modules/bare-env": { | |
| 46 | "version": "3.0.0", | |
| 47 | "resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz", | |
| 48 | "integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==", | |
| 49 | "license": "Apache-2.0", | |
| 50 | "dependencies": { | |
| 51 | "bare-os": "^3.0.1" | |
| 52 | } | |
| 53 | }, | |
| 54 | "node_modules/bare-events": { | |
| 55 | "version": "2.9.1", | |
| 56 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", | |
| 57 | "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", | |
| 58 | "license": "Apache-2.0", | |
| 59 | "peerDependencies": { | |
| 60 | "bare-abort-controller": "*" | |
| 61 | }, | |
| 62 | "peerDependenciesMeta": { | |
| 63 | "bare-abort-controller": { | |
| 64 | "optional": true | |
| 65 | } | |
| 66 | } | |
| 67 | }, | |
| 68 | "node_modules/bare-fs": { | |
| 69 | "version": "4.7.3", | |
| 70 | "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.3.tgz", | |
| 71 | "integrity": "sha512-xRgplks8SvcKkdlv2M6Z2LZmRsmqd+x0nXXGXeMEjwdibj1HSDrlnqBRLeYdMvsgCox7Bq0e+DHwfczOfsn6IA==", | |
| 72 | "license": "Apache-2.0", | |
| 73 | "dependencies": { | |
| 74 | "bare-events": "^2.5.4", | |
| 75 | "bare-path": "^3.0.0", | |
| 76 | "bare-stream": "^2.6.4", | |
| 77 | "bare-url": "^2.2.2", | |
| 78 | "fast-fifo": "^1.3.2" | |
| 79 | }, | |
| 80 | "engines": { | |
| 81 | "bare": ">=1.16.0" | |
| 82 | }, | |
| 83 | "peerDependencies": { | |
| 84 | "bare-buffer": "*" | |
| 85 | }, | |
| 86 | "peerDependenciesMeta": { | |
| 87 | "bare-buffer": { | |
| 88 | "optional": true | |
| 89 | } | |
| 90 | } | |
| 91 | }, | |
| 92 | "node_modules/bare-hrtime": { | |
| 93 | "version": "2.1.1", | |
| 94 | "resolved": "https://registry.npmjs.org/bare-hrtime/-/bare-hrtime-2.1.1.tgz", | |
| 95 | "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==", | |
| 96 | "license": "Apache-2.0" | |
| 97 | }, | |
| 98 | "node_modules/bare-net": { | |
| 99 | "version": "2.3.2", | |
| 100 | "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.2.tgz", | |
| 101 | "integrity": "sha512-I+yz+pqbYsBkxDsnu5vkKvy7RSNY9CcAvu2jZT6PsmdXJQG1i3dmD5V7xc3334OVp2absgtUEYLmmuNFlphBzg==", | |
| 102 | "license": "Apache-2.0", | |
| 103 | "dependencies": { | |
| 104 | "bare-events": "^2.2.2", | |
| 105 | "bare-pipe": "^4.0.0", | |
| 106 | "bare-stream": "^2.0.0", | |
| 107 | "bare-tcp": "^2.0.0" | |
| 108 | } | |
| 109 | }, | |
| 110 | "node_modules/bare-os": { | |
| 111 | "version": "3.9.3", | |
| 112 | "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.3.tgz", | |
| 113 | "integrity": "sha512-fF4Q7QsyKVF5Rj0qvI8BgUNjqzC2JvQlpTaPLjVJVxYVUX5Zr9un+y3w1HmA4nNKdFmRBT8z/WmrjvXzXVerKQ==", | |
| 114 | "license": "Apache-2.0", | |
| 115 | "engines": { | |
| 116 | "bare": ">=1.14.0" | |
| 117 | } | |
| 118 | }, | |
| 119 | "node_modules/bare-path": { | |
| 120 | "version": "3.0.1", | |
| 121 | "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", | |
| 122 | "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", | |
| 123 | "license": "Apache-2.0", | |
| 124 | "dependencies": { | |
| 125 | "bare-os": "^3.0.1" | |
| 126 | } | |
| 127 | }, | |
| 128 | "node_modules/bare-pipe": { | |
| 129 | "version": "4.2.2", | |
| 130 | "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-4.2.2.tgz", | |
| 131 | "integrity": "sha512-Wkf6//CmKS8inHrkgi3X/xNMeaGfs2I0ayLM0B8cbPB4DxYHcoes6Ng+muCBfXNIJ9qmEjwDDJjAUz7nRxxjgQ==", | |
| 132 | "license": "Apache-2.0", | |
| 133 | "peer": true, | |
| 134 | "dependencies": { | |
| 135 | "bare-events": "^2.0.0", | |
| 136 | "bare-stream": "^2.0.0" | |
| 137 | }, | |
| 138 | "engines": { | |
| 139 | "bare": ">=1.16.0" | |
| 140 | } | |
| 141 | }, | |
| 142 | "node_modules/bare-posix": { | |
| 143 | "version": "1.0.1", | |
| 144 | "resolved": "https://registry.npmjs.org/bare-posix/-/bare-posix-1.0.1.tgz", | |
| 145 | "integrity": "sha512-b4quA8dyRVvX0aFIAIiev5zFATHwIUGr42Gmt+CqiBNg/bdsNkrj4bbkeotMOMiljJS4DCME5kywJO/TFwKo1A==", | |
| 146 | "license": "Apache-2.0" | |
| 147 | }, | |
| 148 | "node_modules/bare-process": { | |
| 149 | "version": "4.5.0", | |
| 150 | "resolved": "https://registry.npmjs.org/bare-process/-/bare-process-4.5.0.tgz", | |
| 151 | "integrity": "sha512-NTtYDiwleJjWWNg4+yzS6B/RovL/LLPHVvoOM+18W+dYAXCmky0zFfN66hqqBbrlGAlb52/QgKrdzPR3x1QyHA==", | |
| 152 | "license": "Apache-2.0", | |
| 153 | "dependencies": { | |
| 154 | "bare-abort": "^2.0.13", | |
| 155 | "bare-env": "^3.0.0", | |
| 156 | "bare-events": "^2.3.1", | |
| 157 | "bare-hrtime": "^2.0.0", | |
| 158 | "bare-os": "^3.7.1", | |
| 159 | "bare-posix": "^1.0.1", | |
| 160 | "bare-signals": "^4.0.0", | |
| 161 | "bare-stdio": "^1.0.1" | |
| 162 | } | |
| 163 | }, | |
| 164 | "node_modules/bare-signals": { | |
| 165 | "version": "4.2.0", | |
| 166 | "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-4.2.0.tgz", | |
| 167 | "integrity": "sha512-fNHMOdQIlYuTvMB3Oh9Apk99hLKn351+Ir8vz+khiPTcOqIyGG4uWWjdLTzxWdYGsA0eT+We3y0K74hjj2nq7A==", | |
| 168 | "license": "Apache-2.0", | |
| 169 | "dependencies": { | |
| 170 | "bare-events": "^2.5.3", | |
| 171 | "bare-os": "^3.3.1" | |
| 172 | }, | |
| 173 | "engines": { | |
| 174 | "bare": ">=1.7.0" | |
| 175 | } | |
| 176 | }, | |
| 177 | "node_modules/bare-stdio": { | |
| 178 | "version": "1.0.2", | |
| 179 | "resolved": "https://registry.npmjs.org/bare-stdio/-/bare-stdio-1.0.2.tgz", | |
| 180 | "integrity": "sha512-3WJDqtvVGP4f+j68kyEC05umOYNwKJ1xG+YAXL8yZ605WgNqiRhVaFq+mVIhBt2eKNp7pa5vCQdhOt1pNh79SA==", | |
| 181 | "license": "Apache-2.0", | |
| 182 | "dependencies": { | |
| 183 | "bare-fs": "^4.5.2", | |
| 184 | "bare-pipe": "^4.1.5", | |
| 185 | "bare-tty": "^5.0.3" | |
| 186 | } | |
| 187 | }, | |
| 188 | "node_modules/bare-stream": { | |
| 189 | "version": "2.13.3", | |
| 190 | "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", | |
| 191 | "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", | |
| 192 | "license": "Apache-2.0", | |
| 193 | "dependencies": { | |
| 194 | "b4a": "^1.8.1", | |
| 195 | "streamx": "^2.25.0", | |
| 196 | "teex": "^1.0.1" | |
| 197 | }, | |
| 198 | "peerDependencies": { | |
| 199 | "bare-abort-controller": "*", | |
| 200 | "bare-buffer": "*", | |
| 201 | "bare-events": "*" | |
| 202 | }, | |
| 203 | "peerDependenciesMeta": { | |
| 204 | "bare-abort-controller": { | |
| 205 | "optional": true | |
| 206 | }, | |
| 207 | "bare-buffer": { | |
| 208 | "optional": true | |
| 209 | }, | |
| 210 | "bare-events": { | |
| 211 | "optional": true | |
| 212 | } | |
| 213 | } | |
| 214 | }, | |
| 215 | "node_modules/bare-tcp": { | |
| 216 | "version": "2.5.1", | |
| 217 | "resolved": "https://registry.npmjs.org/bare-tcp/-/bare-tcp-2.5.1.tgz", | |
| 218 | "integrity": "sha512-N32OosAegmGzksr/huxIYLU/uaUARte6utO+vAu/EPQztMM7iAC55VeQSfzr2/XBJoz2OIETMvmU7/sdfiOGjw==", | |
| 219 | "license": "Apache-2.0", | |
| 220 | "dependencies": { | |
| 221 | "bare-dns": "^2.0.4", | |
| 222 | "bare-events": "^2.5.4", | |
| 223 | "bare-stream": "^2.6.4" | |
| 224 | }, | |
| 225 | "engines": { | |
| 226 | "bare": ">=1.16.0" | |
| 227 | }, | |
| 228 | "peerDependencies": { | |
| 229 | "bare-pipe": "*" | |
| 230 | }, | |
| 231 | "peerDependenciesMeta": { | |
| 232 | "bare-pipe": { | |
| 233 | "optional": true | |
| 234 | } | |
| 235 | } | |
| 236 | }, | |
| 237 | "node_modules/bare-tty": { | |
| 238 | "version": "5.1.1", | |
| 239 | "resolved": "https://registry.npmjs.org/bare-tty/-/bare-tty-5.1.1.tgz", | |
| 240 | "integrity": "sha512-cXCMAlcLCFTCWwCtXBIhDuXa4hutTEbrHHIK5pvSSb4BLaSGJ9tvxVN3mvGOvXXqONS7l04mTiP+eIUlZKI92g==", | |
| 241 | "license": "Apache-2.0", | |
| 242 | "dependencies": { | |
| 243 | "bare-events": "^2.2.0", | |
| 244 | "bare-signals": "^4.0.0", | |
| 245 | "bare-stream": "^2.0.0" | |
| 246 | }, | |
| 247 | "engines": { | |
| 248 | "bare": ">=1.16.0" | |
| 249 | } | |
| 250 | }, | |
| 251 | "node_modules/bare-url": { | |
| 252 | "version": "2.4.5", | |
| 253 | "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", | |
| 254 | "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", | |
| 255 | "license": "Apache-2.0", | |
| 256 | "dependencies": { | |
| 257 | "bare-path": "^3.0.0" | |
| 258 | } | |
| 259 | }, | |
| 260 | "node_modules/events-universal": { | |
| 261 | "version": "1.0.1", | |
| 262 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", | |
| 263 | "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", | |
| 264 | "license": "Apache-2.0", | |
| 265 | "dependencies": { | |
| 266 | "bare-events": "^2.7.0" | |
| 267 | } | |
| 268 | }, | |
| 269 | "node_modules/fast-fifo": { | |
| 270 | "version": "1.3.2", | |
| 271 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", | |
| 272 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", | |
| 273 | "license": "MIT" | |
| 274 | }, | |
| 275 | "node_modules/streamx": { | |
| 276 | "version": "2.28.0", | |
| 277 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", | |
| 278 | "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", | |
| 279 | "license": "MIT", | |
| 280 | "dependencies": { | |
| 281 | "events-universal": "^1.0.0", | |
| 282 | "fast-fifo": "^1.3.2", | |
| 283 | "text-decoder": "^1.1.0" | |
| 284 | } | |
| 285 | }, | |
| 286 | "node_modules/teex": { | |
| 287 | "version": "1.0.1", | |
| 288 | "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", | |
| 289 | "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", | |
| 290 | "license": "MIT", | |
| 291 | "dependencies": { | |
| 292 | "streamx": "^2.12.5" | |
| 293 | } | |
| 294 | }, | |
| 295 | "node_modules/text-decoder": { | |
| 296 | "version": "1.2.7", | |
| 297 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", | |
| 298 | "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", | |
| 299 | "license": "Apache-2.0", | |
| 300 | "dependencies": { | |
| 301 | "b4a": "^1.6.4" | |
| 302 | } | |
| 303 | } | |
| 304 | } | |
| 305 | } |
| … | … | @@ -0,0 +1,12 @@ |
| 1 | { | |
| 2 | "name": "hyper-atomspace-m0", | |
| 3 | "version": "0.0.0", | |
| 4 | "private": true, | |
| 5 | "description": "M0 UDS bridge experiment: Bare daemon (in-memory s-expr log) for HyperStorageNode", | |
| 6 | "type": "module", | |
| 7 | "dependencies": { | |
| 8 | "bare-fs": "^4.7.3", | |
| 9 | "bare-net": "^2.3.2", | |
| 10 | "bare-process": "^4.5.0" | |
| 11 | } | |
| 12 | } |
| … | … | @@ -0,0 +1,40 @@ |
| 1 | #!/usr/bin/env bash | |
| 2 | # M0 end-to-end: build the C++ harness, start the Bare daemon, round-trip a small | |
| 3 | # AtomSpace through the UDS bridge, gold-check identity. Exits 0 on PASS. | |
| 4 | set -euo pipefail | |
| 5 | cd "$(dirname "$0")" | |
| 6 | ||
| 7 | # Short socket path: AF_UNIX sun_path caps at ~108 bytes, so NOT the scratchpad. | |
| 8 | SOCK="$(mktemp -u /tmp/m0-XXXXXX.sock)" | |
| 9 | DLOG="$(mktemp /tmp/m0-daemon-XXXXXX.log)" | |
| 10 | DPID="" | |
| 11 | ||
| 12 | cleanup() { | |
| 13 | # `bare` is an nvm shim that execs a child runtime, so killing $DPID alone can | |
| 14 | # leave the child. Also match the daemon by its unique (mktemp) socket path. | |
| 15 | [ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true | |
| 16 | pkill -9 -f "daemon.js ${SOCK}\$" 2>/dev/null || true | |
| 17 | rm -f "$SOCK" "$DLOG" | |
| 18 | } | |
| 19 | trap cleanup EXIT | |
| 20 | ||
| 21 | # 1. deps + build | |
| 22 | [ -d node_modules/bare-net ] || npm install --silent | |
| 23 | g++ -std=gnu++20 -O2 -I/usr/local/include m0_roundtrip.cc \ | |
| 24 | -L/usr/local/lib/opencog -Wl,-rpath,/usr/local/lib/opencog \ | |
| 25 | -latomspace -lsexpr -latombase -latom_types \ | |
| 26 | -o m0_roundtrip | |
| 27 | ||
| 28 | # 2. start the Bare daemon, wait for its readiness line | |
| 29 | bare daemon.js "$SOCK" > "$DLOG" 2>&1 & | |
| 30 | DPID=$! | |
| 31 | for _ in $(seq 1 100); do | |
| 32 | grep -q listening "$DLOG" && break | |
| 33 | sleep 0.05 | |
| 34 | done | |
| 35 | if ! grep -q listening "$DLOG"; then | |
| 36 | echo "daemon failed to start:"; cat "$DLOG"; exit 1 | |
| 37 | fi | |
| 38 | ||
| 39 | # 3. run the harness (build src AtomSpace -> UDS -> replay -> gold check) | |
| 40 | ./m0_roundtrip "$SOCK" |