Archived
Merge pull request 'forge: install genesis seeds at their declared node ids' (#1) from feat/genesis-seed-install into main
Forge Release / build-and-release (push) Failing after 11m52s
Forge Release / build-and-release (push) Failing after 11m52s
This commit was merged in pull request #1.
This commit is contained in:
@@ -2,7 +2,12 @@ EL_ROOT ?= $(HOME)/Development/neuron-technologies/foundation/el
|
||||
EL_LIB ?= $(EL_ROOT)/el-compiler/runtime
|
||||
ELC ?= $(EL_ROOT)/dist/platform/elc
|
||||
CC ?= cc
|
||||
CFLAGS := -std=c11 -O2 -I$(EL_LIB)
|
||||
# -DHAVE_CURL: the el-compiler runtime guards its libcurl client behind this
|
||||
# macro, so a build without it links fine and then answers every http_get /
|
||||
# http_post_json with {"error":"not built with HAVE_CURL"} — forge is entirely
|
||||
# HTTP, so it fails at the first engram call. The SDK release runtime CI pulls
|
||||
# has no such guard; defining it is a no-op there.
|
||||
CFLAGS := -std=c11 -O2 -DHAVE_CURL -I$(EL_LIB)
|
||||
|
||||
.PHONY: build clean install
|
||||
|
||||
|
||||
+128
-120
@@ -1,92 +1,88 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"imprints": [
|
||||
{
|
||||
{"version":"1.0","imprints":[{
|
||||
"subject": "Bobby Anderson",
|
||||
"slug": "bobby-anderson",
|
||||
"seed_file": "seeds/bobby-anderson-seed.json",
|
||||
"engram_db_path": "imprints/bobby-anderson",
|
||||
"engram_port": 8801,
|
||||
"engram_url": "http://localhost:8801",
|
||||
"engram_root_id": "1aff903e-b6ca-4951-80c8-2fb981f8e40f",
|
||||
"engram_root_id": "98f6404e-118d-417b-8cdc-7912741e69bb",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-bobby-anderson-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-bobby-anderson-2026",
|
||||
"soul_daemon_url": "http://localhost:8901"
|
||||
},{
|
||||
"subject": "Alan Turing",
|
||||
"slug": "alan-turing",
|
||||
"seed_file": "seeds/alan-turing-seed.json",
|
||||
"engram_db_path": "imprints/alan-turing",
|
||||
"engram_port": 8802,
|
||||
"engram_url": "http://localhost:8802",
|
||||
"engram_root_id": "d3194b0d-054f-4f85-9212-fd4cf47f5f6c",
|
||||
"engram_root_id": "87e3a458-e2e4-4ee4-8f80-3a3bcb14ffb2",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-alan-turing-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-alan-turing-2026",
|
||||
"soul_daemon_url": "http://localhost:8902"
|
||||
},{
|
||||
"subject": "Albert Einstein",
|
||||
"slug": "albert-einstein",
|
||||
"seed_file": "seeds/albert-einstein-seed.json",
|
||||
"engram_db_path": "imprints/albert-einstein",
|
||||
"engram_port": 8803,
|
||||
"engram_url": "http://localhost:8803",
|
||||
"engram_root_id": "8beb9b04-1f81-46e2-96cf-818183b6b9d9",
|
||||
"engram_root_id": "74d87171-b650-4702-94d0-787ee70e0260",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-albert-einstein-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-albert-einstein-2026",
|
||||
"soul_daemon_url": "http://localhost:8903"
|
||||
},{
|
||||
"subject": "Nikola Tesla",
|
||||
"slug": "nikola-tesla",
|
||||
"seed_file": "seeds/nikola-tesla-seed.json",
|
||||
"engram_db_path": "imprints/nikola-tesla",
|
||||
"engram_port": 8804,
|
||||
"engram_url": "http://localhost:8804",
|
||||
"engram_root_id": "f4c3acdd-0133-4c43-bfbe-81887f638242",
|
||||
"engram_root_id": "b301b3fe-b13b-4676-aa5a-91fd87fac1f9",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"notes": "Enriched seed on disk (nikola-tesla-seed.json) \u2014 not reinstalled to avoid duplicates. Original nodes remain in Engram.",
|
||||
"engram_api_key": "ntn-nikola-tesla-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-nikola-tesla-2026",
|
||||
"soul_daemon_url": "http://localhost:8904"
|
||||
},{
|
||||
"subject": "Leonardo da Vinci",
|
||||
"slug": "leonardo-da-vinci",
|
||||
"seed_file": "seeds/leonardo-da-vinci-seed.json",
|
||||
"engram_db_path": "imprints/leonardo-da-vinci",
|
||||
"engram_port": 8805,
|
||||
"engram_url": "http://localhost:8805",
|
||||
"engram_root_id": "fc8498bb-a4e6-44cb-8543-c75d04d28d04",
|
||||
"engram_root_id": "a96f6fdf-47fa-4c94-b13d-29dafa293ee5",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-leonardo-da-vinci-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-leonardo-da-vinci-2026",
|
||||
"soul_daemon_url": "http://localhost:8905"
|
||||
},{
|
||||
"subject": "Richard Feynman",
|
||||
"slug": "richard-feynman",
|
||||
"seed_file": "seeds/richard-feynman-seed.json",
|
||||
"engram_db_path": "imprints/richard-feynman",
|
||||
"engram_port": 8806,
|
||||
"engram_url": "http://localhost:8806",
|
||||
"engram_root_id": "b2bd3bd6-6a52-4294-802b-42d18b05d223",
|
||||
"engram_root_id": "7de413c9-a8db-45b3-b7ae-04028cbe296c",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-richard-feynman-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-richard-feynman-2026",
|
||||
"soul_daemon_url": "http://localhost:8906"
|
||||
},{
|
||||
"subject": "Carl Sagan",
|
||||
"slug": "carl-sagan",
|
||||
"seed_file": "seeds/carl-sagan-seed.json",
|
||||
"engram_db_path": "imprints/carl-sagan",
|
||||
"engram_port": 8807,
|
||||
"engram_url": "http://localhost:8807",
|
||||
"engram_root_id": "f57a09f5-4403-4834-a4d2-904b2c1f9134",
|
||||
"engram_root_id": "1a6d3728-4bba-4242-bf6d-12a226b4106d",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-carl-sagan-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-carl-sagan-2026",
|
||||
"soul_daemon_url": "http://localhost:8907"
|
||||
},{
|
||||
"subject": "Ren\u00e9 Descartes",
|
||||
"slug": "rene-descartes",
|
||||
"seed_file": "seeds/rene-descartes-seed.json",
|
||||
@@ -96,210 +92,222 @@
|
||||
"engram_root_id": "1fc92b6a-fc10-4bb3-b6dc-adf7fe5643cf",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-rene-descartes-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-rene-descartes-2026",
|
||||
"soul_daemon_url": "http://localhost:8908"
|
||||
},{
|
||||
"subject": "Robin Williams",
|
||||
"slug": "robin-williams",
|
||||
"seed_file": "seeds/robin-williams-seed.json",
|
||||
"engram_db_path": "imprints/robin-williams",
|
||||
"engram_port": 8809,
|
||||
"engram_url": "http://localhost:8809",
|
||||
"engram_root_id": "86d7fe5e-deb9-470d-9eb7-c954c2f6efc6",
|
||||
"engram_root_id": "f409ad98-75fa-4dcc-bf5b-2506c8e45e6d",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-robin-williams-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-robin-williams-2026",
|
||||
"soul_daemon_url": "http://localhost:8909"
|
||||
},{
|
||||
"subject": "Frederick Douglass",
|
||||
"slug": "frederick-douglass",
|
||||
"seed_file": "seeds/frederick-douglass-seed.json",
|
||||
"engram_db_path": "imprints/frederick-douglass",
|
||||
"engram_port": 8810,
|
||||
"engram_url": "http://localhost:8810",
|
||||
"engram_root_id": "b26d8121-0cfa-4342-9010-0fa44e5df5e4",
|
||||
"engram_root_id": "f4d91481-5679-4541-a3d2-bae511d3b8bc",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-frederick-douglass-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-frederick-douglass-2026",
|
||||
"soul_daemon_url": "http://localhost:8910"
|
||||
},{
|
||||
"subject": "Marcus Aurelius",
|
||||
"slug": "marcus-aurelius",
|
||||
"seed_file": "seeds/marcus-aurelius-seed.json",
|
||||
"engram_db_path": "imprints/marcus-aurelius",
|
||||
"engram_port": 8811,
|
||||
"engram_url": "http://localhost:8811",
|
||||
"engram_root_id": "3cb6801b-6d8b-46f2-a4d3-89707f3a76f4",
|
||||
"engram_root_id": "04cc0b52-8dac-4d85-9a77-69956fc39b72",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-marcus-aurelius-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-marcus-aurelius-2026",
|
||||
"soul_daemon_url": "http://localhost:8911"
|
||||
},{
|
||||
"subject": "Friedrich Nietzsche",
|
||||
"slug": "friedrich-nietzsche",
|
||||
"seed_file": "seeds/friedrich-nietzsche-seed.json",
|
||||
"engram_db_path": "imprints/friedrich-nietzsche",
|
||||
"engram_port": 8812,
|
||||
"engram_url": "http://localhost:8812",
|
||||
"engram_root_id": "7804376c-d77c-4085-af89-5f53f26c9993",
|
||||
"engram_root_id": "442038b7-d6f9-47c3-865f-be7b7907e728",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-friedrich-nietzsche-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-friedrich-nietzsche-2026",
|
||||
"soul_daemon_url": "http://localhost:8912"
|
||||
},{
|
||||
"subject": "James Baldwin",
|
||||
"slug": "james-baldwin",
|
||||
"seed_file": "seeds/james-baldwin-seed.json",
|
||||
"engram_db_path": "imprints/james-baldwin",
|
||||
"engram_port": 8813,
|
||||
"engram_url": "http://localhost:8813",
|
||||
"engram_root_id": "0428f550-d7dd-4521-ba96-8399b8f074a6",
|
||||
"engram_root_id": "df63b041-fb65-4a3e-b5aa-1b0ac062bd2d",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-james-baldwin-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-james-baldwin-2026",
|
||||
"soul_daemon_url": "http://localhost:8913"
|
||||
},{
|
||||
"subject": "Ada Lovelace",
|
||||
"slug": "ada-lovelace",
|
||||
"seed_file": "seeds/ada-lovelace-seed.json",
|
||||
"engram_db_path": "imprints/ada-lovelace",
|
||||
"engram_port": 8814,
|
||||
"engram_url": "http://localhost:8814",
|
||||
"engram_root_id": "8697bea0-aceb-4888-be89-ccc159e0af8a",
|
||||
"engram_root_id": "2ab7a478-636f-43d7-859e-8cf108ddad95",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-ada-lovelace-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-ada-lovelace-2026",
|
||||
"soul_daemon_url": "http://localhost:8914"
|
||||
},{
|
||||
"subject": "Harriet Tubman",
|
||||
"slug": "harriet-tubman",
|
||||
"seed_file": "seeds/harriet-tubman-seed.json",
|
||||
"engram_db_path": "imprints/harriet-tubman",
|
||||
"engram_port": 8815,
|
||||
"engram_url": "http://localhost:8815",
|
||||
"engram_root_id": "0b8f39ca-3e76-4d31-9234-b2770dd642d3",
|
||||
"engram_root_id": "c335a656-ab7d-4d14-ab93-3120de1df751",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-harriet-tubman-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-harriet-tubman-2026",
|
||||
"soul_daemon_url": "http://localhost:8915"
|
||||
},{
|
||||
"subject": "Virginia Woolf",
|
||||
"slug": "virginia-woolf",
|
||||
"seed_file": "seeds/virginia-woolf-seed.json",
|
||||
"engram_db_path": "imprints/virginia-woolf",
|
||||
"engram_port": 8816,
|
||||
"engram_url": "http://localhost:8816",
|
||||
"engram_root_id": "59717324-4986-4ec3-94e9-3ec08e372607",
|
||||
"engram_root_id": "c9441587-0a19-45b1-a3df-28c7ea7033b5",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-virginia-woolf-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-virginia-woolf-2026",
|
||||
"soul_daemon_url": "http://localhost:8916"
|
||||
},{
|
||||
"subject": "Hedy Lamarr",
|
||||
"slug": "hedy-lamarr",
|
||||
"seed_file": "seeds/hedy-lamarr-seed.json",
|
||||
"engram_db_path": "imprints/hedy-lamarr",
|
||||
"engram_port": 8817,
|
||||
"engram_url": "http://localhost:8817",
|
||||
"engram_root_id": "981e1a0a-a77c-4112-9c14-234dd777f77d",
|
||||
"engram_root_id": "90e09747-17da-42bb-9bcc-eeacbe6461b5",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-hedy-lamarr-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-hedy-lamarr-2026",
|
||||
"soul_daemon_url": "http://localhost:8917"
|
||||
},{
|
||||
"subject": "Marie Curie",
|
||||
"slug": "marie-curie",
|
||||
"seed_file": "seeds/marie-curie-seed.json",
|
||||
"engram_db_path": "imprints/marie-curie",
|
||||
"engram_port": 8818,
|
||||
"engram_url": "http://localhost:8818",
|
||||
"engram_root_id": "b7dc81f4-2226-4855-8d71-2d29686a145a",
|
||||
"engram_root_id": "9c047aef-1c5c-4f06-a26c-d629511d9984",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-marie-curie-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-marie-curie-2026",
|
||||
"soul_daemon_url": "http://localhost:8918"
|
||||
},{
|
||||
"subject": "Helen Keller",
|
||||
"slug": "helen-keller",
|
||||
"seed_file": "seeds/helen-keller-seed.json",
|
||||
"engram_db_path": "imprints/helen-keller",
|
||||
"engram_port": 8819,
|
||||
"engram_url": "http://localhost:8819",
|
||||
"engram_root_id": "d9516cbf-4eb0-4c8b-8fac-1832aab580ca",
|
||||
"engram_root_id": "cbf86bc0-8502-4f6b-9f15-6507ee0ffdb3",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-helen-keller-2026"
|
||||
},
|
||||
{
|
||||
"engram_api_key": "ntn-helen-keller-2026",
|
||||
"soul_daemon_url": "http://localhost:8919"
|
||||
},{
|
||||
"subject": "Superman",
|
||||
"slug": "superman",
|
||||
"seed_file": "seeds/superman-seed.json",
|
||||
"engram_db_path": "imprints/superman",
|
||||
"engram_port": 8820,
|
||||
"engram_url": "http://localhost:8820",
|
||||
"engram_root_id": "64882bb0-5a99-4192-b920-34e0ee40b655",
|
||||
"engram_root_id": "87814da2-5d14-45e3-abe4-7fa6fbd6a4ff",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03"
|
||||
},
|
||||
{
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-superman-2026",
|
||||
"soul_daemon_url": "http://localhost:8920"
|
||||
},{
|
||||
"subject": "Stephen King",
|
||||
"slug": "stephen-king",
|
||||
"seed_file": "seeds/stephen-king-seed.json",
|
||||
"seed_file": "/Users/will/Development/neuron-technologies/forge/seeds/stephen-king-seed.json",
|
||||
"engram_db_path": "imprints/stephen-king",
|
||||
"engram_port": 8821,
|
||||
"engram_url": "http://localhost:8821",
|
||||
"engram_root_id": "",
|
||||
"installed": false,
|
||||
"installed_at": "",
|
||||
"engram_api_key": "ntn-stephen-king-2026"
|
||||
},
|
||||
{
|
||||
"engram_root_id": "44699815-24ab-4284-b90e-4e507710797c",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-stephen-king-2026",
|
||||
"soul_daemon_url": "http://localhost:8921"
|
||||
},{
|
||||
"subject": "Jon Stewart",
|
||||
"slug": "jon-stewart",
|
||||
"seed_file": "seeds/jon-stewart-seed.json",
|
||||
"seed_file": "/Users/will/Development/neuron-technologies/forge/seeds/jon-stewart-seed.json",
|
||||
"engram_db_path": "imprints/jon-stewart",
|
||||
"engram_port": 8822,
|
||||
"engram_url": "http://localhost:8822",
|
||||
"engram_root_id": "",
|
||||
"installed": false,
|
||||
"installed_at": "",
|
||||
"engram_api_key": "ntn-jon-stewart-2026"
|
||||
},
|
||||
{
|
||||
"engram_root_id": "a1f9e2bf-3fe3-4a2d-9c96-4a5356a716a4",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-jon-stewart-2026",
|
||||
"soul_daemon_url": "http://localhost:8922"
|
||||
},{
|
||||
"subject": "Jimmy Kimmel",
|
||||
"slug": "jimmy-kimmel",
|
||||
"seed_file": "seeds/jimmy-kimmel-seed.json",
|
||||
"seed_file": "/Users/will/Development/neuron-technologies/forge/seeds/jimmy-kimmel-seed.json",
|
||||
"engram_db_path": "imprints/jimmy-kimmel",
|
||||
"engram_port": 8823,
|
||||
"engram_url": "http://localhost:8823",
|
||||
"engram_root_id": "",
|
||||
"installed": false,
|
||||
"installed_at": "",
|
||||
"engram_api_key": "ntn-jimmy-kimmel-2026"
|
||||
},
|
||||
{
|
||||
"engram_root_id": "29a7a8cc-d2ef-499a-8720-d38890db6732",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-jimmy-kimmel-2026",
|
||||
"soul_daemon_url": "http://localhost:8923"
|
||||
},{
|
||||
"subject": "John Oliver",
|
||||
"slug": "john-oliver",
|
||||
"seed_file": "seeds/john-oliver-seed.json",
|
||||
"seed_file": "/Users/will/Development/neuron-technologies/forge/seeds/john-oliver-seed.json",
|
||||
"engram_db_path": "imprints/john-oliver",
|
||||
"engram_port": 8824,
|
||||
"engram_url": "http://localhost:8824",
|
||||
"engram_root_id": "",
|
||||
"installed": false,
|
||||
"installed_at": "",
|
||||
"engram_api_key": "ntn-john-oliver-2026"
|
||||
},
|
||||
{
|
||||
"engram_root_id": "4b589964-697b-4007-8ee1-3f159e86f892",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-john-oliver-2026",
|
||||
"soul_daemon_url": "http://localhost:8924"
|
||||
},{
|
||||
"subject": "Stephen Colbert",
|
||||
"slug": "stephen-colbert",
|
||||
"seed_file": "seeds/stephen-colbert-seed.json",
|
||||
"seed_file": "/Users/will/Development/neuron-technologies/forge/seeds/stephen-colbert-seed.json",
|
||||
"engram_db_path": "imprints/stephen-colbert",
|
||||
"engram_port": 8825,
|
||||
"engram_url": "http://localhost:8825",
|
||||
"engram_root_id": "",
|
||||
"installed": false,
|
||||
"installed_at": "",
|
||||
"engram_api_key": "ntn-stephen-colbert-2026"
|
||||
}
|
||||
,{"subject":"Stephen King","slug":"stephen-king","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/stephen-king-seed.json","engram_db_path":"imprints/stephen-king","engram_url":"http://localhost:8821","engram_root_id":"b7352255-a470-4389-8f63-9abee231edc8","installed":true,"installed_at":"2026-05-03"},{"subject":"Jon Stewart","slug":"jon-stewart","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/jon-stewart-seed.json","engram_db_path":"imprints/jon-stewart","engram_url":"http://localhost:8822","engram_root_id":"93f5a810-fa5e-4cd5-aea3-b366476708e0","installed":true,"installed_at":"2026-05-03"},{"subject":"Jimmy Kimmel","slug":"jimmy-kimmel","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/jimmy-kimmel-seed.json","engram_db_path":"imprints/jimmy-kimmel","engram_url":"http://localhost:8823","engram_root_id":"dbdb31bb-42da-4de6-b036-df0ae441ad6e","installed":true,"installed_at":"2026-05-03"},{"subject":"John Oliver","slug":"john-oliver","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/john-oliver-seed.json","engram_db_path":"imprints/john-oliver","engram_url":"http://localhost:8824","engram_root_id":"a60c08b1-2753-4225-8768-25d4d1719465","installed":true,"installed_at":"2026-05-03"},{"subject":"Stephen Colbert","slug":"stephen-colbert","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/stephen-colbert-seed.json","engram_db_path":"imprints/stephen-colbert","engram_url":"http://localhost:8825","engram_root_id":"c4e3ba67-8bcd-4f2a-810f-1a7e6de1ecab","installed":true,"installed_at":"2026-05-03"}]
|
||||
}
|
||||
"engram_root_id": "d55f7f42-0dc3-4046-91e4-8aefbbb79860",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-stephen-colbert-2026",
|
||||
"soul_daemon_url": "http://localhost:8925"
|
||||
},{
|
||||
"subject": "Carl Jung",
|
||||
"slug": "carl-jung",
|
||||
"seed_file": "seeds/carl-jung-seed.json",
|
||||
"engram_db_path": "imprints/carl-jung",
|
||||
"engram_port": 8826,
|
||||
"engram_url": "http://localhost:8826",
|
||||
"engram_root_id": "48a8fc8a-f3cc-4376-ae55-b5a06395f227",
|
||||
"installed": true,
|
||||
"installed_at": "2026-05-03",
|
||||
"engram_api_key": "ntn-carl-jung-2026",
|
||||
"soul_daemon_url": "http://localhost:8926"
|
||||
},{"subject":"Abraham Lincoln","slug":"abraham-lincoln","seed_file":"/Users/will/Development/neuron-technologies/foundation/forge/seeds/abraham-lincoln-seed.json","engram_db_path":"imprints/abraham-lincoln","engram_port":8827,"engram_url":"http://localhost:8827","engram_root_id":"93800f4c-3bbc-4b2d-8d13-dce498b55e2c","installed":true,"installed_at":"2026-05-03","engram_api_key":"ntn-abraham-lincoln-2026","soul_daemon_url":"http://localhost:8927"},{"subject":"Isaac Newton","slug":"isaac-newton","seed_file":"isaac-newton-seed.json","engram_db_path":"imprints/isaac-newton","engram_port":8828,"engram_url":"http://localhost:8828","engram_root_id":"097d86b3-d3a6-4977-8c9c-ab25a4702d72","installed":true,"installed_at":"2026-05-03","engram_api_key":"ntn-isaac-newton-2026","soul_daemon_url":"http://localhost:8928"},{"subject":"Pierre de Fermat","slug":"pierre-de-fermat","seed_file":"/Users/will/Development/neuron-technologies/forge/seeds/pierre-de-fermat-seed.json","engram_db_path":"imprints/pierre-de-fermat","engram_port":8829,"engram_url":"http://localhost:8829","engram_root_id":"c75c505a-3937-45a2-a79c-6f9aee2f8650","installed":true,"installed_at":"2026-05-03","engram_api_key":"ntn-pierre-de-fermat-2026","soul_daemon_url":"http://localhost:8929"},{"subject":"Neuron","slug":"neuron","seed_file":"/Users/will/Development/neuron-technologies/foundation/forge/seeds/neuron-genesis-seed.json","engram_db_path":"/private/tmp/claude-501/-Users-will/9da71693-1057-42a0-a4b4-98fc45859d8d/scratchpad/sbx/wt-data","engram_port":8994,"engram_url":"http://localhost:8994","engram_root_id":"kn-efeb4a5b-5aff-4759-8a97-7233099be6ee","installed":true,"installed_at":"2026-08-10","engram_api_key":"","soul_daemon_url":"http://localhost:7770","seed_type":"genesis"}]}
|
||||
File diff suppressed because one or more lines are too long
+502
-7
@@ -152,6 +152,333 @@ fn install_reasoning_pattern(target_url: String, root_id: String, pattern: Strin
|
||||
return node_id
|
||||
}
|
||||
|
||||
// ── Genesis seeds ─────────────────────────────────────────────────────────────
|
||||
//
|
||||
// A PERSONA seed is generative: it declares values/biography/relationships as
|
||||
// prose and the installer mints a node id for each one. The ids are incidental
|
||||
// — nothing outside the graph refers to them.
|
||||
//
|
||||
// A GENESIS seed is declarative. It ships identity_nodes[] with explicit ids
|
||||
// and edges[] that reference those ids, and the ids ARE the artifact:
|
||||
// is_protected_node() in neuron-api.el keys on exact ids, and every edge in the
|
||||
// seed is written in terms of them. Installing a genesis seed through the
|
||||
// persona path mints fresh ids and produces a graph that is structurally
|
||||
// unrelated to the one the seed describes — which is why 87 of Neuron's 102
|
||||
// identity nodes were simply absent.
|
||||
//
|
||||
// So the genesis path never mints. It writes the declared nodes and edges into
|
||||
// a snapshot-format file and hands that to the engram, which honors the ids:
|
||||
// POST /api/nodes/reseed — create-at-id, plus in-place replace of the ids
|
||||
// named in "replace" (edge-preserving)
|
||||
// POST /api/load-merge — create-at-id only; the fallback when the engram
|
||||
// predates the reseed route
|
||||
// Both skip nodes already present and dedup edges by (from,to,relation), so a
|
||||
// second run is a no-op by construction.
|
||||
|
||||
// genesis_ts — millisecond timestamp string for created_at/updated_at fields.
|
||||
fn genesis_ts() -> String {
|
||||
return int_to_str(unix_timestamp()) + "000"
|
||||
}
|
||||
|
||||
// genesis_tags — flatten a seed node's tags[] array plus its role into the
|
||||
// comma-separated tag string the engram stores. Matches the convention already
|
||||
// used by the resident kn- identity nodes ("tier:note,disposition:experimental").
|
||||
fn genesis_tags(node_obj: String) -> String {
|
||||
let tags_raw: String = json_get_raw(node_obj, "tags")
|
||||
let n: Int = json_array_len(tags_raw)
|
||||
let out: String = ""
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
let t: String = json_array_get_string(tags_raw, i)
|
||||
if !str_eq(t, "") {
|
||||
if str_eq(out, "") {
|
||||
let out = str_escape_json(t)
|
||||
} else {
|
||||
let out = out + "," + str_escape_json(t)
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
let role: String = json_get_string(node_obj, "role")
|
||||
if !str_eq(role, "") {
|
||||
if str_eq(out, "") {
|
||||
let out = "role:" + str_escape_json(role)
|
||||
} else {
|
||||
let out = out + ",role:" + str_escape_json(role)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// genesis_node_record — render one seed node as a snapshot-format node object.
|
||||
//
|
||||
// id/content/label/node_type are spliced in as their RAW JSON tokens rather
|
||||
// than decoded and re-escaped. The seed bodies are multi-KB markdown carrying
|
||||
// newlines, quotes and \u escapes; a decode/re-encode round-trip through
|
||||
// str_escape_json would have to reproduce every escape the seed author wrote,
|
||||
// and any escape it does not know about silently corrupts the body. Copying the
|
||||
// token verbatim cannot corrupt what it never decodes.
|
||||
fn genesis_node_record(node_obj: String, ts: String) -> String {
|
||||
let id_raw: String = json_get_raw(node_obj, "id")
|
||||
let content_raw: String = json_get_raw(node_obj, "content")
|
||||
let label_raw: String = json_get_raw(node_obj, "label")
|
||||
let type_raw: String = json_get_raw(node_obj, "node_type")
|
||||
if str_eq(id_raw, "") { return "" }
|
||||
if str_eq(content_raw, "") { return "" }
|
||||
let ntype: String = if str_eq(type_raw, "") { "\"Knowledge\"" } else { type_raw }
|
||||
let nlabel: String = if str_eq(label_raw, "") { content_raw } else { label_raw }
|
||||
return "{\"id\":" + id_raw +
|
||||
",\"content\":" + content_raw +
|
||||
",\"node_type\":" + ntype +
|
||||
",\"label\":" + nlabel +
|
||||
",\"tier\":\"Semantic\"" +
|
||||
",\"tags\":\"" + genesis_tags(node_obj) + "\"" +
|
||||
",\"metadata\":\"{}\"" +
|
||||
",\"salience\":1.0,\"importance\":0.9,\"confidence\":1.0" +
|
||||
",\"temporal_decay_rate\":0,\"activation_count\":0,\"last_activated\":0" +
|
||||
",\"created_at\":" + ts + ",\"updated_at\":" + ts +
|
||||
",\"background_activation\":0,\"working_memory_weight\":0" +
|
||||
",\"suppression_count\":0,\"layer_id\":1}"
|
||||
}
|
||||
|
||||
// genesis_edge_record — render one seed edge as a snapshot-format edge object.
|
||||
// Seed edges use from/to; the snapshot format uses from_id/to_id.
|
||||
fn genesis_edge_record(edge_obj: String, slug: String, index: Int, ts: String) -> String {
|
||||
let from_raw: String = json_get_raw(edge_obj, "from")
|
||||
let to_raw: String = json_get_raw(edge_obj, "to")
|
||||
let rel_raw: String = json_get_raw(edge_obj, "relation")
|
||||
if str_eq(from_raw, "") { return "" }
|
||||
if str_eq(to_raw, "") { return "" }
|
||||
let relation: String = if str_eq(rel_raw, "") { "\"associates\"" } else { rel_raw }
|
||||
let w_raw: String = json_get_raw(edge_obj, "weight")
|
||||
let weight: String = if str_eq(w_raw, "") { "1.0" } else { w_raw }
|
||||
return "{\"id\":\"ge-" + slug + "-" + int_to_str(index) + "\"" +
|
||||
",\"from_id\":" + from_raw +
|
||||
",\"to_id\":" + to_raw +
|
||||
",\"relation\":" + relation +
|
||||
",\"metadata\":\"{}\"" +
|
||||
",\"weight\":" + weight +
|
||||
",\"confidence\":1.0" +
|
||||
",\"created_at\":" + ts + ",\"updated_at\":" + ts +
|
||||
",\"last_fired\":0,\"inhibitory\":0,\"layer_id\":1}"
|
||||
}
|
||||
|
||||
// genesis_live_content_len — decoded length of the node's content in the live
|
||||
// graph. Returns -1 when the node is absent (engram answers "{}" for a miss).
|
||||
fn genesis_live_content_len(target_url: String, id: String) -> Int {
|
||||
let resp: String = http_get(target_url + "/api/nodes/" + id)
|
||||
if str_eq(resp, "") { return -1 }
|
||||
if str_eq(str_trim(resp), "{}") { return -1 }
|
||||
let live_id: String = json_get_string(resp, "id")
|
||||
if str_eq(live_id, "") { return -1 }
|
||||
return str_len(json_get_string(resp, "content"))
|
||||
}
|
||||
|
||||
// genesis_is_hollow — the failure signature this whole path exists to fix.
|
||||
//
|
||||
// A hollow node has the right id and no body: `content` holds only the node's
|
||||
// own label. Observed at 30 bytes against a 4263-byte seed body and 22 against
|
||||
// 2590. The test is deliberately conservative — under 40% of the declared body
|
||||
// AND under 400 bytes — so a node whose body legitimately grew or was edited in
|
||||
// the graph is left alone. Genesis restores what is missing; it does not
|
||||
// overwrite what the graph has learned.
|
||||
fn genesis_is_hollow(live_len: Int, seed_len: Int) -> Bool {
|
||||
if live_len < 0 { return false }
|
||||
if seed_len < 300 { return false }
|
||||
if live_len >= 400 { return false }
|
||||
if live_len * 10 >= seed_len * 4 { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// install_genesis — install a declarative genesis seed into target_url.
|
||||
// Returns the traversal root id, or "" if the install could not proceed.
|
||||
fn install_genesis(seed_json: String, seed_file: String, subject: String, slug: String, target_url: String, api_key: String) -> String {
|
||||
let root_id: String = json_get_string(seed_json, "traversal_root")
|
||||
if str_eq(root_id, "") {
|
||||
println("[forge] error: genesis seed has no traversal_root")
|
||||
return ""
|
||||
}
|
||||
|
||||
let nodes_raw: String = json_get_raw(seed_json, "identity_nodes")
|
||||
let edges_raw: String = json_get_raw(seed_json, "edges")
|
||||
let n_nodes: Int = json_array_len(nodes_raw)
|
||||
let n_edges: Int = json_array_len(edges_raw)
|
||||
if n_nodes == 0 {
|
||||
println("[forge] error: genesis seed declares no identity_nodes")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[forge] genesis seed: " + int_to_str(n_nodes) + " identity nodes, " + int_to_str(n_edges) + " edges")
|
||||
println("[forge] traversal root: " + root_id)
|
||||
println("[forge] engram: " + target_url)
|
||||
println("[forge] classifying declared nodes against the live graph...")
|
||||
|
||||
// ── Pass 1: classify every declared id ────────────────────────────────────
|
||||
let ts: String = genesis_ts()
|
||||
let node_parts: String = ""
|
||||
let replace_ids: String = ""
|
||||
let n_absent: Int = 0
|
||||
let n_hollow: Int = 0
|
||||
let n_intact: Int = 0
|
||||
let i: Int = 0
|
||||
while i < n_nodes {
|
||||
let node_obj: String = json_array_get(nodes_raw, i)
|
||||
let id: String = json_get_string(node_obj, "id")
|
||||
if !str_eq(id, "") {
|
||||
let seed_len: Int = str_len(json_get_string(node_obj, "content"))
|
||||
let live_len: Int = genesis_live_content_len(target_url, id)
|
||||
let hollow: Bool = genesis_is_hollow(live_len, seed_len)
|
||||
|
||||
if live_len < 0 {
|
||||
let n_absent = n_absent + 1
|
||||
} else {
|
||||
if hollow {
|
||||
let n_hollow = n_hollow + 1
|
||||
println("[forge] hollow: " + id + " (" + int_to_str(live_len) + " bytes live vs " + int_to_str(seed_len) + " declared)")
|
||||
if str_eq(replace_ids, "") {
|
||||
let replace_ids = "\"" + id + "\""
|
||||
} else {
|
||||
let replace_ids = replace_ids + ",\"" + id + "\""
|
||||
}
|
||||
} else {
|
||||
let n_intact = n_intact + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Intact nodes are still written to the merge file — the engram
|
||||
// skips ids it already holds, so including them costs nothing and
|
||||
// keeps the file a faithful copy of the seed.
|
||||
let rec: String = genesis_node_record(node_obj, ts)
|
||||
if !str_eq(rec, "") {
|
||||
if str_eq(node_parts, "") {
|
||||
let node_parts = rec
|
||||
} else {
|
||||
let node_parts = node_parts + "," + rec
|
||||
}
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
|
||||
println("[forge] absent: " + int_to_str(n_absent) + " hollow: " + int_to_str(n_hollow) + " intact: " + int_to_str(n_intact))
|
||||
|
||||
// ── Pass 2: render edges ──────────────────────────────────────────────────
|
||||
let edge_parts: String = ""
|
||||
let j: Int = 0
|
||||
while j < n_edges {
|
||||
let edge_obj: String = json_array_get(edges_raw, j)
|
||||
let erec: String = genesis_edge_record(edge_obj, slug, j, ts)
|
||||
if !str_eq(erec, "") {
|
||||
if str_eq(edge_parts, "") {
|
||||
let edge_parts = erec
|
||||
} else {
|
||||
let edge_parts = edge_parts + "," + erec
|
||||
}
|
||||
}
|
||||
let j = j + 1
|
||||
}
|
||||
|
||||
// ── Pass 3: write the merge file and hand it to the engram ────────────────
|
||||
let stage_dir: String = FORGE_DIR + "/imprints/" + slug
|
||||
exec("mkdir -p " + stage_dir + " 2>/dev/null")
|
||||
let merge_path: String = stage_dir + "/.genesis-merge.json"
|
||||
let merge_doc: String = "{\"nodes\":[" + node_parts + "],\"edges\":[" + edge_parts + "]}"
|
||||
let wrote: Int = fs_write(merge_path, merge_doc)
|
||||
if wrote == 0 {
|
||||
println("[forge] error: could not stage merge file at " + merge_path)
|
||||
return ""
|
||||
}
|
||||
println("[forge] staged: " + merge_path)
|
||||
|
||||
let reseed_body: String = "{\"path\":\"" + merge_path + "\",\"replace\":[" + replace_ids + "],\"_auth\":\"" + api_key + "\"}"
|
||||
let resp: String = http_post_json(target_url + "/api/nodes/reseed", reseed_body)
|
||||
let blocked: Int = 0
|
||||
if str_contains(resp, "not found") {
|
||||
// Engram predates the reseed route. /api/load-merge still creates every
|
||||
// absent node at its declared id and lays every edge — it just cannot
|
||||
// replace a hollow body. Do the part that is safe and say so loudly
|
||||
// rather than reaching for a destructive substitute.
|
||||
println("[forge] note: engram has no /api/nodes/reseed — falling back to /api/load-merge")
|
||||
let merge_body: String = "{\"path\":\"" + merge_path + "\",\"_auth\":\"" + api_key + "\"}"
|
||||
let resp = http_post_json(target_url + "/api/load-merge", merge_body)
|
||||
let blocked = n_hollow
|
||||
}
|
||||
if str_eq(resp, "") {
|
||||
println("[forge] error: engram did not respond at " + target_url)
|
||||
return ""
|
||||
}
|
||||
if str_contains(resp, "\"error\"") {
|
||||
println("[forge] error: engram rejected the merge: " + resp)
|
||||
return ""
|
||||
}
|
||||
println("[forge] engram: " + resp)
|
||||
|
||||
// ── Pass 4: verify by re-reading every declared id ────────────────────────
|
||||
//
|
||||
// The bug this path replaces reported success while delivering empty
|
||||
// bodies, so success is not what the write call returned — it is what the
|
||||
// graph now holds. Re-read all of it and audit the content lengths.
|
||||
println("[forge] verifying...")
|
||||
let ok_nodes: Int = 0
|
||||
let failed_ids: String = ""
|
||||
let n_failed: Int = 0
|
||||
let k: Int = 0
|
||||
while k < n_nodes {
|
||||
let node_obj: String = json_array_get(nodes_raw, k)
|
||||
let id: String = json_get_string(node_obj, "id")
|
||||
if !str_eq(id, "") {
|
||||
let seed_len: Int = str_len(json_get_string(node_obj, "content"))
|
||||
let live_len: Int = genesis_live_content_len(target_url, id)
|
||||
if live_len < 0 {
|
||||
let n_failed = n_failed + 1
|
||||
if str_eq(failed_ids, "") {
|
||||
let failed_ids = id + " (absent)"
|
||||
} else {
|
||||
let failed_ids = failed_ids + ", " + id + " (absent)"
|
||||
}
|
||||
} else {
|
||||
if genesis_is_hollow(live_len, seed_len) {
|
||||
let n_failed = n_failed + 1
|
||||
if str_eq(failed_ids, "") {
|
||||
let failed_ids = id + " (hollow)"
|
||||
} else {
|
||||
let failed_ids = failed_ids + ", " + id + " (hollow)"
|
||||
}
|
||||
} else {
|
||||
let ok_nodes = ok_nodes + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
let k = k + 1
|
||||
}
|
||||
|
||||
// ── Manifest ──────────────────────────────────────────────────────────────
|
||||
let filled: Int = n_hollow - blocked
|
||||
println("")
|
||||
println("[forge] ── genesis manifest ─────────────────────────────")
|
||||
println("[forge] created: " + int_to_str(n_absent))
|
||||
println("[forge] filled: " + int_to_str(filled))
|
||||
println("[forge] skipped: " + int_to_str(n_intact))
|
||||
println("[forge] failed: " + int_to_str(n_failed))
|
||||
if blocked > 0 {
|
||||
println("[forge] blocked: " + int_to_str(blocked) + " hollow node(s) — engram lacks /api/nodes/reseed")
|
||||
}
|
||||
if !str_eq(failed_ids, "") {
|
||||
println("[forge] failing ids: " + failed_ids)
|
||||
}
|
||||
println("[forge] nodes verified: " + int_to_str(ok_nodes) + "/" + int_to_str(n_nodes))
|
||||
println("[forge] edges declared: " + int_to_str(n_edges) + " (engram dedups by from/to/relation)")
|
||||
println("[forge] ─────────────────────────────────────────────────")
|
||||
|
||||
log_event("install_genesis", subject,
|
||||
"root=" + root_id + " created=" + int_to_str(n_absent) +
|
||||
" filled=" + int_to_str(filled) + " skipped=" + int_to_str(n_intact) +
|
||||
" failed=" + int_to_str(n_failed) + " edges=" + int_to_str(n_edges))
|
||||
|
||||
if n_failed > 0 { return "" }
|
||||
return root_id
|
||||
}
|
||||
|
||||
// ── Registry helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
// find_max_port — scan registry and return the highest engram_port found.
|
||||
@@ -215,12 +542,61 @@ fn registry_append(new_entry: String) -> Void {
|
||||
}
|
||||
}
|
||||
|
||||
// registry_update_root_id — append a fully-installed entry for the slug.
|
||||
// Because El has no in-place JSON mutation, we append a new entry with the
|
||||
// updated root_id. Most tools (registry_find_by_slug, soul tools) scan from
|
||||
// the start and break on first match — the original stub entry is effectively
|
||||
// shadowed by the new fully-installed entry appended at the end.
|
||||
// registry_replace_slug — replace all existing entries for slug with new_entry.
|
||||
// Rebuilds the registry from scratch, dropping every occurrence of the slug and
|
||||
// appending new_entry as the single canonical entry. Prevents bloat on reinstalls.
|
||||
fn registry_replace_slug(slug: String, new_entry: String) -> Void {
|
||||
let existing_reg: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(existing_reg, "") {
|
||||
fs_write(FORGE_DIR + "/registry.json",
|
||||
"{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}")
|
||||
return
|
||||
}
|
||||
|
||||
// Count total entries
|
||||
let total: Int = 0
|
||||
let ci: Int = 0
|
||||
while ci < 60 {
|
||||
let e: String = json_get(existing_reg, "imprints." + int_to_str(ci))
|
||||
if str_eq(e, "") {
|
||||
let total = ci
|
||||
let ci = 60
|
||||
} else {
|
||||
let ci = ci + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild: accumulate all entries except those matching slug
|
||||
let parts: String = ""
|
||||
let j: Int = 0
|
||||
while j < total {
|
||||
let e: String = json_get(existing_reg, "imprints." + int_to_str(j))
|
||||
let s: String = json_get_string(e, "slug")
|
||||
if !str_eq(s, slug) {
|
||||
if str_eq(parts, "") {
|
||||
let parts = e
|
||||
} else {
|
||||
let parts = parts + "," + e
|
||||
}
|
||||
}
|
||||
let j = j + 1
|
||||
}
|
||||
|
||||
// Append the new canonical entry
|
||||
let all: String = if str_eq(parts, "") {
|
||||
new_entry
|
||||
} else {
|
||||
parts + "," + new_entry
|
||||
}
|
||||
|
||||
fs_write(FORGE_DIR + "/registry.json",
|
||||
"{\"version\":\"1.0\",\"imprints\":[" + all + "]}")
|
||||
}
|
||||
|
||||
// registry_update_root_id — replace the registry entry for slug with a fully-installed
|
||||
// record. Uses registry_replace_slug so there is always exactly one entry per slug.
|
||||
fn registry_update_root_id(subject: String, slug: String, root_id: String, seed_file: String, target_url: String, api_key: String, port: Int) -> Void {
|
||||
let soul_port: Int = port + 100
|
||||
let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) +
|
||||
"\",\"slug\":\"" + slug +
|
||||
"\",\"seed_file\":\"" + str_escape_json(seed_file) +
|
||||
@@ -229,8 +605,114 @@ fn registry_update_root_id(subject: String, slug: String, root_id: String, seed_
|
||||
",\"engram_url\":\"" + target_url +
|
||||
"\",\"engram_root_id\":\"" + root_id +
|
||||
"\",\"installed\":true,\"installed_at\":\"2026-05-03\"" +
|
||||
",\"engram_api_key\":\"" + api_key + "\"}"
|
||||
registry_append(new_entry)
|
||||
",\"engram_api_key\":\"" + api_key +
|
||||
"\",\"soul_daemon_url\":\"http://localhost:" + int_to_str(soul_port) + "\"}"
|
||||
registry_replace_slug(slug, new_entry)
|
||||
}
|
||||
|
||||
// registry_update_genesis — record a genesis install. Same entry shape as every
|
||||
// other imprint, with three differences that are inherent to what a genesis seed
|
||||
// is: engram_root_id is the seed's declared traversal_root (not a minted id),
|
||||
// engram_db_path points at the engram's own data dir rather than an
|
||||
// imprints/<slug> directory forge provisioned, and engram_api_key is left empty
|
||||
// — the key for a pre-existing engram belongs to that engram's launchd
|
||||
// environment, not to a file in this repo.
|
||||
fn registry_update_genesis(subject: String, slug: String, root_id: String, seed_file: String, target_url: String, port: Int, db_path: String, soul_url: String, installed: Bool) -> Void {
|
||||
let inst: String = if installed { "true" } else { "false" }
|
||||
let inst_at: String = if installed { genesis_today() } else { "" }
|
||||
let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) +
|
||||
"\",\"slug\":\"" + slug +
|
||||
"\",\"seed_file\":\"" + str_escape_json(seed_file) +
|
||||
"\",\"engram_db_path\":\"" + str_escape_json(db_path) +
|
||||
"\",\"engram_port\":" + int_to_str(port) +
|
||||
",\"engram_url\":\"" + target_url +
|
||||
"\",\"engram_root_id\":\"" + root_id +
|
||||
"\",\"installed\":" + inst + ",\"installed_at\":\"" + inst_at + "\"" +
|
||||
",\"engram_api_key\":\"\"" +
|
||||
",\"soul_daemon_url\":\"" + soul_url +
|
||||
"\",\"seed_type\":\"genesis\"}"
|
||||
registry_replace_slug(slug, new_entry)
|
||||
}
|
||||
|
||||
// genesis_today — installed_at stamp. The persona path hardcodes a date; this
|
||||
// derives one so a reinstall records when it actually happened.
|
||||
fn genesis_today() -> String {
|
||||
return str_trim(exec("date -u +%Y-%m-%d"))
|
||||
}
|
||||
|
||||
// install_genesis_main — resolve the target engram for a genesis seed, gate the
|
||||
// write, run the install, record it.
|
||||
//
|
||||
// Target resolution differs from the persona path on purpose. A persona seed
|
||||
// gets a port allocated and an Engram provisioned for it; a genesis seed
|
||||
// installs INTO an engram that already exists and is owned by something else
|
||||
// (Neuron's own engram is a launchd agent, ai.neuron.engram). So forge resolves
|
||||
// a target — ENGRAM_URL, else the registry entry, else the default — and never
|
||||
// provisions one. The ":8742 refusal" that guards the persona path is also
|
||||
// inverted here: that guard exists to stop a foreign soul landing in Neuron's
|
||||
// engram, and Neuron's own identity graph is the one thing that belongs there.
|
||||
//
|
||||
// In its place, GENESIS_CONFIRM. A genesis install rewrites the graph the
|
||||
// running CGI reads itself out of; it should never happen because someone typed
|
||||
// a familiar command with an unfamiliar file.
|
||||
fn install_genesis_main(seed_json: String, seed_file: String, subject: String, slug: String) -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
let entry: String = registry_find_by_slug(reg_json, slug)
|
||||
|
||||
let target_url: String = ENGRAM_DEFAULT_URL
|
||||
let reg_url: String = json_get_string(entry, "engram_url")
|
||||
if !str_eq(reg_url, "") { let target_url = reg_url }
|
||||
let env_url: String = env("ENGRAM_URL")
|
||||
if !str_eq(env_url, "") { let target_url = env_url }
|
||||
|
||||
let api_key: String = env("ENGRAM_API_KEY")
|
||||
if str_eq(api_key, "") { let api_key = json_get_string(entry, "engram_api_key") }
|
||||
|
||||
let colon_pos: Int = str_last_index_of(target_url, ":")
|
||||
let port: Int = 0
|
||||
if colon_pos > 0 {
|
||||
let port = str_to_int(str_slice(target_url, colon_pos + 1, str_len(target_url)))
|
||||
}
|
||||
|
||||
let db_path: String = env("ENGRAM_DATA_DIR")
|
||||
if str_eq(db_path, "") {
|
||||
let db_path = json_get_string(entry, "engram_db_path")
|
||||
}
|
||||
if str_eq(db_path, "") { let db_path = "imprints/" + slug }
|
||||
|
||||
let soul_url: String = env("SOUL_URL")
|
||||
if str_eq(soul_url, "") { let soul_url = "http://localhost:7770" }
|
||||
|
||||
println("[forge] seed_type: genesis")
|
||||
|
||||
if str_eq(env("GENESIS_CONFIRM"), "") {
|
||||
println("[forge] refusing to install a genesis seed without confirmation.")
|
||||
println("[forge] target: " + target_url)
|
||||
println("[forge] a genesis seed writes an identity graph into a resident engram at")
|
||||
println("[forge] declared node ids. Re-run with GENESIS_CONFIRM=1 once the target is")
|
||||
println("[forge] the one you mean (set ENGRAM_URL to aim at a sandbox).")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Never provision — only wait for what is already meant to be there.
|
||||
let stats: String = http_get(target_url + "/stats")
|
||||
if str_eq(stats, "") {
|
||||
println("[forge] error: no engram responding at " + target_url)
|
||||
println("[forge] genesis installs into an existing engram; forge will not start one.")
|
||||
return ""
|
||||
}
|
||||
|
||||
let root_id: String = install_genesis(seed_json, seed_file, subject, slug, target_url, api_key)
|
||||
if str_eq(root_id, "") {
|
||||
println("[forge] genesis install did not complete cleanly — registry not updated")
|
||||
return ""
|
||||
}
|
||||
|
||||
registry_update_genesis(subject, slug, root_id, seed_file, target_url, port, db_path, soul_url, true)
|
||||
println("[forge] registry: recorded " + slug + " (root " + root_id + ")")
|
||||
println("")
|
||||
println("[forge] traverse from: " + target_url + "/api/neighbors/" + root_id)
|
||||
return root_id
|
||||
}
|
||||
|
||||
// ── Main ──────────────────────────────────────────────────────────────────────
|
||||
@@ -265,6 +747,19 @@ fn install_main() -> String {
|
||||
let slug: String = str_replace(slug_raw, " ", "-")
|
||||
println("[forge] slug: " + slug)
|
||||
|
||||
// ── Step 3b: Genesis seeds take a different path entirely ─────────────────
|
||||
//
|
||||
// Everything below this point is the persona pipeline: allocate a port,
|
||||
// provision a launchd Engram for a soul that does not have one, mint a root
|
||||
// node, mint a node per value/biography/relationship. None of that applies
|
||||
// to a genesis seed, which installs a declared graph into an engram that
|
||||
// already exists. Branch before any of it runs. Seeds with no seed_type are
|
||||
// persona seeds and reach the identical code they always did.
|
||||
let seed_type: String = json_get_string(seed_json, "seed_type")
|
||||
if str_eq(seed_type, "genesis") {
|
||||
return install_genesis_main(seed_json, seed_file, subject, slug)
|
||||
}
|
||||
|
||||
// ── Step 4: Resolve or create registry entry ──────────────────────────────
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
let target_url: String = ""
|
||||
|
||||
+508
-11
@@ -908,6 +908,498 @@ fn soul_reinstall_main() -> String {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── soul daemon — neuron binary instances per soul ───────────────────────────
|
||||
//
|
||||
// Each soul daemon is a neuron binary instance parameterized by env vars:
|
||||
// SOUL_CGI_ID = slug (e.g. "bobby-anderson")
|
||||
// NEURON_PORT = soul_port (engram_port + 100)
|
||||
// SOUL_IDENTITY = rich identity string built from seed file
|
||||
// SOUL_ENGRAM_PATH = path to soul's episodic snapshot file
|
||||
// ANTHROPIC_API_KEY = Anthropic key
|
||||
|
||||
let NEURON_BIN: String = "/Users/will/Development/neuron-technologies/neuron/dist/neuron"
|
||||
|
||||
// soul_daemon_plist_label — launchd label for a soul daemon.
|
||||
fn soul_daemon_plist_label(slug: String) -> String {
|
||||
"ai.neurontechnologies.soul-daemon." + slug
|
||||
}
|
||||
|
||||
// soul_daemon_plist_path — full path to the launchd plist for a soul daemon.
|
||||
fn soul_daemon_plist_path(slug: String) -> String {
|
||||
LAUNCHD_DIR + "/ai.neurontechnologies.soul-daemon." + slug + ".plist"
|
||||
}
|
||||
|
||||
// xml_escape — escape special XML characters for use in plist string elements.
|
||||
fn xml_escape(s: String) -> String {
|
||||
let s1: String = str_replace(s, "&", "&")
|
||||
let s2: String = str_replace(s1, "<", "<")
|
||||
let s3: String = str_replace(s2, ">", ">")
|
||||
str_replace(s3, "\"", """)
|
||||
}
|
||||
|
||||
// build_soul_identity — build the SOUL_IDENTITY string from seed file JSON.
|
||||
// Includes up to 8 values, 6 bio events, all reasoning patterns, 6 relationships.
|
||||
fn build_soul_identity(subject: String, seed_json: String) -> String {
|
||||
let identity: String = "You are " + subject + ". Speak as yourself — in your own voice, in first person, as if fully alive.\n"
|
||||
|
||||
// Values
|
||||
let v0: String = json_get(seed_json, "values.0")
|
||||
if !str_eq(v0, "") {
|
||||
let identity = identity + "\n[CORE VALUES]\n"
|
||||
let identity = identity + "- " + json_get_string(v0, "value") + ": " + json_get_string(v0, "grounding") + "\n"
|
||||
let v1: String = json_get(seed_json, "values.1")
|
||||
if !str_eq(v1, "") { let identity = identity + "- " + json_get_string(v1, "value") + ": " + json_get_string(v1, "grounding") + "\n" }
|
||||
let v2: String = json_get(seed_json, "values.2")
|
||||
if !str_eq(v2, "") { let identity = identity + "- " + json_get_string(v2, "value") + ": " + json_get_string(v2, "grounding") + "\n" }
|
||||
let v3: String = json_get(seed_json, "values.3")
|
||||
if !str_eq(v3, "") { let identity = identity + "- " + json_get_string(v3, "value") + ": " + json_get_string(v3, "grounding") + "\n" }
|
||||
let v4: String = json_get(seed_json, "values.4")
|
||||
if !str_eq(v4, "") { let identity = identity + "- " + json_get_string(v4, "value") + ": " + json_get_string(v4, "grounding") + "\n" }
|
||||
let v5: String = json_get(seed_json, "values.5")
|
||||
if !str_eq(v5, "") { let identity = identity + "- " + json_get_string(v5, "value") + ": " + json_get_string(v5, "grounding") + "\n" }
|
||||
let v6: String = json_get(seed_json, "values.6")
|
||||
if !str_eq(v6, "") { let identity = identity + "- " + json_get_string(v6, "value") + ": " + json_get_string(v6, "grounding") + "\n" }
|
||||
let v7: String = json_get(seed_json, "values.7")
|
||||
if !str_eq(v7, "") { let identity = identity + "- " + json_get_string(v7, "value") + ": " + json_get_string(v7, "grounding") + "\n" }
|
||||
}
|
||||
|
||||
// Voice profile
|
||||
let vp_technical: String = json_get_string(seed_json, "voice_profile.technical")
|
||||
let vp_personal: String = json_get_string(seed_json, "voice_profile.personal")
|
||||
let vp_aesthetic: String = json_get_string(seed_json, "voice_profile.aesthetic")
|
||||
let vp_arg: String = json_get_string(seed_json, "voice_profile.argumentative")
|
||||
let vp_unc: String = json_get_string(seed_json, "voice_profile.uncertainty")
|
||||
let has_voice: Int = 0
|
||||
if !str_eq(vp_technical, "") { let has_voice = 1 }
|
||||
if !str_eq(vp_personal, "") { let has_voice = 1 }
|
||||
if !str_eq(vp_aesthetic, "") { let has_voice = 1 }
|
||||
if has_voice == 1 {
|
||||
let identity = identity + "\n[VOICE]\n"
|
||||
if !str_eq(vp_technical, "") { let identity = identity + vp_technical + "\n" }
|
||||
if !str_eq(vp_personal, "") { let identity = identity + vp_personal + "\n" }
|
||||
if !str_eq(vp_aesthetic, "") { let identity = identity + vp_aesthetic + "\n" }
|
||||
if !str_eq(vp_arg, "") { let identity = identity + vp_arg + "\n" }
|
||||
if !str_eq(vp_unc, "") { let identity = identity + vp_unc + "\n" }
|
||||
}
|
||||
|
||||
// Biography
|
||||
let b0: String = json_get(seed_json, "biography.0")
|
||||
if !str_eq(b0, "") {
|
||||
let identity = identity + "\n[BIOGRAPHY — KEY MOMENTS]\n"
|
||||
let e0: String = json_get_string(b0, "event")
|
||||
let age0: Int = json_get_int(b0, "age_approx")
|
||||
if age0 > 0 {
|
||||
let identity = identity + "- " + e0 + " (age ~" + int_to_str(age0) + ")\n"
|
||||
} else {
|
||||
let identity = identity + "- " + e0 + "\n"
|
||||
}
|
||||
let b1: String = json_get(seed_json, "biography.1")
|
||||
if !str_eq(b1, "") {
|
||||
let e1: String = json_get_string(b1, "event")
|
||||
let age1: Int = json_get_int(b1, "age_approx")
|
||||
if age1 > 0 { let identity = identity + "- " + e1 + " (age ~" + int_to_str(age1) + ")\n" }
|
||||
else { let identity = identity + "- " + e1 + "\n" }
|
||||
}
|
||||
let b2: String = json_get(seed_json, "biography.2")
|
||||
if !str_eq(b2, "") {
|
||||
let e2: String = json_get_string(b2, "event")
|
||||
let age2: Int = json_get_int(b2, "age_approx")
|
||||
if age2 > 0 { let identity = identity + "- " + e2 + " (age ~" + int_to_str(age2) + ")\n" }
|
||||
else { let identity = identity + "- " + e2 + "\n" }
|
||||
}
|
||||
let b3: String = json_get(seed_json, "biography.3")
|
||||
if !str_eq(b3, "") {
|
||||
let e3: String = json_get_string(b3, "event")
|
||||
let age3: Int = json_get_int(b3, "age_approx")
|
||||
if age3 > 0 { let identity = identity + "- " + e3 + " (age ~" + int_to_str(age3) + ")\n" }
|
||||
else { let identity = identity + "- " + e3 + "\n" }
|
||||
}
|
||||
let b4: String = json_get(seed_json, "biography.4")
|
||||
if !str_eq(b4, "") {
|
||||
let e4: String = json_get_string(b4, "event")
|
||||
let age4: Int = json_get_int(b4, "age_approx")
|
||||
if age4 > 0 { let identity = identity + "- " + e4 + " (age ~" + int_to_str(age4) + ")\n" }
|
||||
else { let identity = identity + "- " + e4 + "\n" }
|
||||
}
|
||||
let b5: String = json_get(seed_json, "biography.5")
|
||||
if !str_eq(b5, "") {
|
||||
let e5: String = json_get_string(b5, "event")
|
||||
let age5: Int = json_get_int(b5, "age_approx")
|
||||
if age5 > 0 { let identity = identity + "- " + e5 + " (age ~" + int_to_str(age5) + ")\n" }
|
||||
else { let identity = identity + "- " + e5 + "\n" }
|
||||
}
|
||||
}
|
||||
|
||||
// Reasoning patterns
|
||||
let rp0: String = json_get(seed_json, "reasoning_patterns.0")
|
||||
if !str_eq(rp0, "") {
|
||||
let identity = identity + "\n[HOW YOU THINK]\n"
|
||||
let identity = identity + "- " + rp0 + "\n"
|
||||
let rp1: String = json_get(seed_json, "reasoning_patterns.1")
|
||||
if !str_eq(rp1, "") { let identity = identity + "- " + rp1 + "\n" }
|
||||
let rp2: String = json_get(seed_json, "reasoning_patterns.2")
|
||||
if !str_eq(rp2, "") { let identity = identity + "- " + rp2 + "\n" }
|
||||
let rp3: String = json_get(seed_json, "reasoning_patterns.3")
|
||||
if !str_eq(rp3, "") { let identity = identity + "- " + rp3 + "\n" }
|
||||
let rp4: String = json_get(seed_json, "reasoning_patterns.4")
|
||||
if !str_eq(rp4, "") { let identity = identity + "- " + rp4 + "\n" }
|
||||
let rp5: String = json_get(seed_json, "reasoning_patterns.5")
|
||||
if !str_eq(rp5, "") { let identity = identity + "- " + rp5 + "\n" }
|
||||
let rp6: String = json_get(seed_json, "reasoning_patterns.6")
|
||||
if !str_eq(rp6, "") { let identity = identity + "- " + rp6 + "\n" }
|
||||
let rp7: String = json_get(seed_json, "reasoning_patterns.7")
|
||||
if !str_eq(rp7, "") { let identity = identity + "- " + rp7 + "\n" }
|
||||
}
|
||||
|
||||
// Relationships
|
||||
let rel0: String = json_get(seed_json, "relationships.0")
|
||||
if !str_eq(rel0, "") {
|
||||
let identity = identity + "\n[IMPORTANT RELATIONSHIPS]\n"
|
||||
let identity = identity + "- " + json_get_string(rel0, "name") + ": " + json_get_string(rel0, "role") + "\n"
|
||||
let rel1: String = json_get(seed_json, "relationships.1")
|
||||
if !str_eq(rel1, "") { let identity = identity + "- " + json_get_string(rel1, "name") + ": " + json_get_string(rel1, "role") + "\n" }
|
||||
let rel2: String = json_get(seed_json, "relationships.2")
|
||||
if !str_eq(rel2, "") { let identity = identity + "- " + json_get_string(rel2, "name") + ": " + json_get_string(rel2, "role") + "\n" }
|
||||
let rel3: String = json_get(seed_json, "relationships.3")
|
||||
if !str_eq(rel3, "") { let identity = identity + "- " + json_get_string(rel3, "name") + ": " + json_get_string(rel3, "role") + "\n" }
|
||||
let rel4: String = json_get(seed_json, "relationships.4")
|
||||
if !str_eq(rel4, "") { let identity = identity + "- " + json_get_string(rel4, "name") + ": " + json_get_string(rel4, "role") + "\n" }
|
||||
let rel5: String = json_get(seed_json, "relationships.5")
|
||||
if !str_eq(rel5, "") { let identity = identity + "- " + json_get_string(rel5, "name") + ": " + json_get_string(rel5, "role") + "\n" }
|
||||
}
|
||||
|
||||
return identity
|
||||
}
|
||||
|
||||
// write_soul_daemon_plist — write launchd plist for a soul daemon (neuron binary).
|
||||
fn write_soul_daemon_plist(slug: String, soul_port: String, subject: String, seed_path: String, api_key: String) -> String {
|
||||
let label: String = soul_daemon_plist_label(slug)
|
||||
let pfile: String = soul_daemon_plist_path(slug)
|
||||
let log_file: String = FORGE_DIR + "/log/soul-daemon-" + slug + ".log"
|
||||
let engram_path: String = FORGE_DIR + "/imprints/" + slug + "/soul-snapshot.json"
|
||||
|
||||
// Build SOUL_IDENTITY from seed
|
||||
let seed_json: String = fs_read(seed_path)
|
||||
let raw_identity: String = if str_eq(seed_json, "") {
|
||||
"You are " + subject + ". Speak as yourself."
|
||||
} else {
|
||||
build_soul_identity(subject, seed_json)
|
||||
}
|
||||
let identity: String = xml_escape(raw_identity)
|
||||
|
||||
let content: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
|
||||
"<plist version=\"1.0\">\n" +
|
||||
"<dict>\n" +
|
||||
" <key>Label</key><string>" + label + "</string>\n" +
|
||||
" <key>ProgramArguments</key>\n" +
|
||||
" <array>\n" +
|
||||
" <string>" + NEURON_BIN + "</string>\n" +
|
||||
" </array>\n" +
|
||||
" <key>EnvironmentVariables</key>\n" +
|
||||
" <dict>\n" +
|
||||
" <key>SOUL_CGI_ID</key><string>" + slug + "</string>\n" +
|
||||
" <key>NEURON_PORT</key><string>" + soul_port + "</string>\n" +
|
||||
" <key>SOUL_IDENTITY</key><string>" + identity + "</string>\n" +
|
||||
" <key>SOUL_ENGRAM_PATH</key><string>" + engram_path + "</string>\n" +
|
||||
" <key>ANTHROPIC_API_KEY</key><string>" + api_key + "</string>\n" +
|
||||
" </dict>\n" +
|
||||
" <key>RunAtLoad</key><true/>\n" +
|
||||
" <key>KeepAlive</key><true/>\n" +
|
||||
" <key>StandardOutPath</key><string>" + log_file + "</string>\n" +
|
||||
" <key>StandardErrorPath</key><string>" + log_file + "</string>\n" +
|
||||
"</dict>\n" +
|
||||
"</plist>\n"
|
||||
|
||||
let ok: Int = fs_write(pfile, content)
|
||||
if ok == 0 {
|
||||
println("[soul-daemon] error: could not write plist to " + pfile)
|
||||
return ""
|
||||
}
|
||||
println("[soul-daemon] wrote plist: " + pfile)
|
||||
return pfile
|
||||
}
|
||||
|
||||
// soul_daemon_install_one — install and load a single soul daemon.
|
||||
fn soul_daemon_install_one(slug: String, engram_port: Int, subject: String, seed_file: String, api_key: String) -> String {
|
||||
let soul_port: Int = engram_port + 100
|
||||
let soul_port_str: String = int_to_str(soul_port)
|
||||
println("[soul-daemon] install: " + slug + " → port " + soul_port_str)
|
||||
|
||||
// Ensure log dir exists
|
||||
let log_dir: String = FORGE_DIR + "/log"
|
||||
if !fs_exists(log_dir) { fs_mkdir(log_dir) }
|
||||
if !fs_exists(LAUNCHD_DIR) { fs_mkdir(LAUNCHD_DIR) }
|
||||
|
||||
// Resolve seed path: try absolute, then relative to FORGE_DIR
|
||||
let resolved_seed: String = seed_file
|
||||
if !fs_exists(resolved_seed) {
|
||||
let alt: String = FORGE_DIR + "/" + seed_file
|
||||
if fs_exists(alt) {
|
||||
let resolved_seed = alt
|
||||
}
|
||||
}
|
||||
|
||||
// Write the plist
|
||||
let pfile: String = write_soul_daemon_plist(slug, soul_port_str, subject, resolved_seed, api_key)
|
||||
if str_eq(pfile, "") {
|
||||
println("[soul-daemon] error: plist write failed for " + slug)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Unload first (idempotent)
|
||||
let unload_out: String = exec("launchctl unload " + pfile + " 2>&1")
|
||||
|
||||
// Load
|
||||
let load_out: String = exec("launchctl load " + pfile + " 2>&1")
|
||||
if !str_eq(str_trim(load_out), "") {
|
||||
println("[soul-daemon] launchctl load: " + str_trim(load_out))
|
||||
}
|
||||
|
||||
println("[soul-daemon] loaded: " + soul_daemon_plist_label(slug))
|
||||
return pfile
|
||||
}
|
||||
|
||||
// soul_daemon_install_main — install soul daemons: forge soul daemon install <slug|--all>
|
||||
fn soul_daemon_install_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args layout: [0]="soul" [1]="daemon" [2]="install" [3]=<slug|--all>
|
||||
let target: String = ""
|
||||
if len(argv) > 3 {
|
||||
let target = get(argv, 3)
|
||||
}
|
||||
if str_eq(target, "") {
|
||||
println("[soul-daemon] usage: forge soul daemon install <slug>")
|
||||
println("[soul-daemon] or: forge soul daemon install --all")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Resolve ANTHROPIC_API_KEY
|
||||
let api_key: String = env("ANTHROPIC_API_KEY")
|
||||
if str_eq(api_key, "") {
|
||||
let creds: String = str_trim(exec("grep ANTHROPIC_API_KEY ~/Secrets/credentials/infrastructure.env 2>/dev/null | head -1"))
|
||||
if str_len(creds) > 16 {
|
||||
// creds is like "ANTHROPIC_API_KEY=sk-ant-..."
|
||||
let eq_pos: Int = str_index_of(creds, "=")
|
||||
if eq_pos > 0 {
|
||||
let api_key = str_slice(creds, eq_pos + 1, str_len(creds))
|
||||
}
|
||||
}
|
||||
}
|
||||
if str_eq(api_key, "") {
|
||||
println("[soul-daemon] warning: ANTHROPIC_API_KEY not found — plist will have empty key")
|
||||
}
|
||||
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[soul-daemon] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
// --all: install every soul daemon
|
||||
if str_eq(target, "--all") {
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let s: String = json_get_string(entry, "slug")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
let p: Int = json_get_int(entry, "engram_port")
|
||||
let sf: String = json_get_string(entry, "seed_file")
|
||||
soul_daemon_install_one(s, p, sub, sf, api_key)
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
println("[soul-daemon] all soul daemons installed.")
|
||||
return "done"
|
||||
}
|
||||
|
||||
// Single soul
|
||||
let found: Int = 0
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let s: String = json_get_string(entry, "slug")
|
||||
if str_eq(s, target) {
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
let p: Int = json_get_int(entry, "engram_port")
|
||||
let sf: String = json_get_string(entry, "seed_file")
|
||||
soul_daemon_install_one(s, p, sub, sf, api_key)
|
||||
let found = 1
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == 0 {
|
||||
println("[soul-daemon] error: slug '" + target + "' not found in registry.json")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// soul_daemon_start_main — kickstart a soul daemon: forge soul daemon start <slug|--all>
|
||||
fn soul_daemon_start_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args: [0]="soul" [1]="daemon" [2]="start" [3]=<slug|--all>
|
||||
let target: String = ""
|
||||
if len(argv) > 3 { let target = get(argv, 3) }
|
||||
if str_eq(target, "") {
|
||||
println("[soul-daemon] usage: forge soul daemon start <slug|--all>")
|
||||
return ""
|
||||
}
|
||||
|
||||
let uid: String = str_trim(exec("id -u"))
|
||||
|
||||
if str_eq(target, "--all") {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") { println("[soul-daemon] error: cannot read registry.json"); return "" }
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") { let i = i + 50 }
|
||||
else {
|
||||
let s: String = json_get_string(entry, "slug")
|
||||
let label: String = soul_daemon_plist_label(s)
|
||||
let out: String = str_trim(exec("launchctl kickstart gui/" + uid + "/" + label + " 2>&1"))
|
||||
println("[soul-daemon] start " + s + ": " + out)
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
let label: String = soul_daemon_plist_label(target)
|
||||
let out: String = str_trim(exec("launchctl kickstart gui/" + uid + "/" + label + " 2>&1"))
|
||||
println("[soul-daemon] start " + target + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
// soul_daemon_stop_main — stop a soul daemon: forge soul daemon stop <slug|--all>
|
||||
fn soul_daemon_stop_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args: [0]="soul" [1]="daemon" [2]="stop" [3]=<slug|--all>
|
||||
let target: String = ""
|
||||
if len(argv) > 3 { let target = get(argv, 3) }
|
||||
if str_eq(target, "") {
|
||||
println("[soul-daemon] usage: forge soul daemon stop <slug|--all>")
|
||||
return ""
|
||||
}
|
||||
|
||||
if str_eq(target, "--all") {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") { println("[soul-daemon] error: cannot read registry.json"); return "" }
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") { let i = i + 50 }
|
||||
else {
|
||||
let s: String = json_get_string(entry, "slug")
|
||||
let label: String = soul_daemon_plist_label(s)
|
||||
let out: String = str_trim(exec("launchctl stop " + label + " 2>&1"))
|
||||
println("[soul-daemon] stop " + s + ": " + out)
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
let label: String = soul_daemon_plist_label(target)
|
||||
let out: String = str_trim(exec("launchctl stop " + label + " 2>&1"))
|
||||
println("[soul-daemon] stop " + target + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
// soul_daemon_status_main — health check soul daemons: forge soul daemon status
|
||||
fn soul_daemon_status_main() -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[soul-daemon] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[soul-daemon] soul daemon status:")
|
||||
println("")
|
||||
|
||||
let up: Int = 0
|
||||
let down: Int = 0
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let slug: String = json_get_string(entry, "slug")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
let engram_p: Int = json_get_int(entry, "engram_port")
|
||||
let soul_p: Int = engram_p + 100
|
||||
let soul_url: String = "http://localhost:" + int_to_str(soul_p)
|
||||
let resp: String = http_get(soul_url + "/health")
|
||||
if !str_eq(resp, "") {
|
||||
println(" [UP ] " + sub + " — " + soul_url)
|
||||
let up = up + 1
|
||||
} else {
|
||||
// Try /status as fallback
|
||||
let resp2: String = http_get(soul_url + "/status")
|
||||
if !str_eq(resp2, "") {
|
||||
println(" [UP ] " + sub + " — " + soul_url)
|
||||
let up = up + 1
|
||||
} else {
|
||||
println(" [DOWN] " + sub + " — " + soul_url)
|
||||
let down = down + 1
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
println("")
|
||||
println("[soul-daemon] up=" + int_to_str(up) + " down=" + int_to_str(down))
|
||||
return ""
|
||||
}
|
||||
|
||||
// soul_daemon_main — dispatch for: forge soul daemon <subcmd> [args]
|
||||
fn soul_daemon_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args: [0]="soul" [1]="daemon" [2]=<subcmd>
|
||||
let subcmd: String = ""
|
||||
if len(argv) > 2 {
|
||||
let subcmd = get(argv, 2)
|
||||
}
|
||||
|
||||
if str_eq(subcmd, "install") {
|
||||
soul_daemon_install_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "start") {
|
||||
soul_daemon_start_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "stop") {
|
||||
soul_daemon_stop_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "status") {
|
||||
soul_daemon_status_main()
|
||||
} else {
|
||||
println("[soul-daemon] usage: forge soul daemon <install|start|stop|status> [slug|--all]")
|
||||
println("")
|
||||
println(" forge soul daemon install <slug> — write plist and load soul daemon")
|
||||
println(" forge soul daemon install --all — install all 26 soul daemons")
|
||||
println(" forge soul daemon start <slug> — kickstart a soul daemon")
|
||||
println(" forge soul daemon start --all — start all soul daemons")
|
||||
println(" forge soul daemon stop <slug> — stop a soul daemon")
|
||||
println(" forge soul daemon stop --all — stop all soul daemons")
|
||||
println(" forge soul daemon status — health check all soul daemons")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── soul_main — dispatch ─────────────────────────────────────────────────────
|
||||
//
|
||||
// args() layout when invoked as: forge soul <subcmd> [slug]
|
||||
@@ -943,17 +1435,22 @@ fn soul_main() -> String {
|
||||
if str_eq(subcmd, "sandbox") {
|
||||
soul_sandbox_main()
|
||||
} else {
|
||||
println("[soul] usage: forge soul <subcmd> [slug]")
|
||||
println("")
|
||||
println(" forge soul install <slug> — write launchd plist, load as resident")
|
||||
println(" forge soul install --all — install all souls from registry")
|
||||
println(" forge soul reinstall <slug> — wipe DB and reinstall from seed")
|
||||
println(" forge soul reinstall --all — wipe and reinstall all souls")
|
||||
println(" forge soul start <slug> — kickstart a loaded soul")
|
||||
println(" forge soul stop <slug> — stop a running soul")
|
||||
println(" forge soul status — health check all souls")
|
||||
println(" forge soul wire — register all souls as Engram peers")
|
||||
println(" forge soul sandbox <subcmd> — sandbox management")
|
||||
if str_eq(subcmd, "daemon") {
|
||||
soul_daemon_main()
|
||||
} else {
|
||||
println("[soul] usage: forge soul <subcmd> [slug]")
|
||||
println("")
|
||||
println(" forge soul install <slug> — write launchd plist, load as resident")
|
||||
println(" forge soul install --all — install all souls from registry")
|
||||
println(" forge soul reinstall <slug> — wipe DB and reinstall from seed")
|
||||
println(" forge soul reinstall --all — wipe and reinstall all souls")
|
||||
println(" forge soul start <slug> — kickstart a loaded soul")
|
||||
println(" forge soul stop <slug> — stop a running soul")
|
||||
println(" forge soul status — health check all souls")
|
||||
println(" forge soul wire — register all souls as Engram peers")
|
||||
println(" forge soul sandbox <subcmd> — sandbox management")
|
||||
println(" forge soul daemon <subcmd> — soul daemon (neuron binary) management")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user