build DHARMA network: 19 soul Engram instances with full peer wiring
- Add scripts/: install_soul.py, install_all.py, launch_dharma.sh, stop_dharma.sh, wire_peers.py - Move all seeds into seeds/ directory (consolidated from root-level scattered files) - Update registry.json with engram_root_id, engram_api_key, engram_url for all 19 installed souls - Add src/soul.el, src/research.el; remove src/daemon.el - .gitignore: exclude imprints/, log/, sandboxes/ (runtime data) - Remove superseded scripts: deploy.py, install_imprints.py, reinstall_imprints.py, fix_collision.py - Remove old root-level seed files and shell scripts superseded by scripts/ versions Key: native engram binary uses _auth body field, per-soul keys ntn-<slug>-2026, DharmaPeer graph edges for peer wiring, sanitize_content() for JSON safety. 190/190 peer pairs wired successfully.
This commit is contained in:
-423
@@ -1,423 +0,0 @@
|
||||
// daemon.el — Soul Engram lifecycle management.
|
||||
//
|
||||
// Manages launchd-resident DHARMA soul Engrams on macOS.
|
||||
//
|
||||
// Commands:
|
||||
// forge daemon install <slug> — write launchd plist, load soul as resident
|
||||
// forge daemon start <slug> — launchctl kickstart the soul
|
||||
// forge daemon stop <slug> — launchctl stop the soul
|
||||
// forge daemon status — health check all souls
|
||||
// forge daemon wire — register all soul Engrams as peers to each other
|
||||
//
|
||||
// Depends on: schema.el (FORGE_VERSION, engram_key, str_escape_json, log_event)
|
||||
|
||||
// ── Constants ──────────────────────────────────────────────────────────────────
|
||||
// (FORGE_DIR is defined in schema.el)
|
||||
|
||||
let ENGRAM_BIN: String = "/Users/will/Development/neuron-technologies/foundation/engram/dist/engram"
|
||||
let LAUNCHD_DIR: String = "/Users/will/Library/LaunchAgents"
|
||||
let DHARMA_API_KEY: String = "ntn-dharma-2026"
|
||||
|
||||
// ── plist generation ───────────────────────────────────────────────────────────
|
||||
|
||||
// plist_label — launchd label for a soul slug.
|
||||
fn plist_label(slug: String) -> String {
|
||||
"ai.neurontechnologies.soul." + slug
|
||||
}
|
||||
|
||||
// plist_path — full path to the launchd plist file for a soul.
|
||||
fn plist_path(slug: String) -> String {
|
||||
LAUNCHD_DIR + "/ai.neurontechnologies.soul." + slug + ".plist"
|
||||
}
|
||||
|
||||
// write_soul_plist — generate and write the launchd plist for a soul.
|
||||
// Returns the path written, or "" on failure.
|
||||
fn write_soul_plist(slug: String, port: String, db_path: String, subject: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let plist_file: String = plist_path(slug)
|
||||
let log_file: String = FORGE_DIR + "/log/" + slug + ".log"
|
||||
let soul_api_key: String = "ntn-" + slug + "-2026"
|
||||
|
||||
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>" + ENGRAM_BIN + "</string>\n" +
|
||||
" </array>\n" +
|
||||
" <key>EnvironmentVariables</key>\n" +
|
||||
" <dict>\n" +
|
||||
" <key>ENGRAM_DB_PATH</key><string>" + db_path + "</string>\n" +
|
||||
" <key>ENGRAM_BIND</key><string>0.0.0.0:" + port + "</string>\n" +
|
||||
" <key>ENGRAM_API_KEY</key><string>" + soul_api_key + "</string>\n" +
|
||||
" <key>ENGRAM_PEER_NAME</key><string>" + str_escape_json(subject) + "</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(plist_file, content)
|
||||
if ok == 0 {
|
||||
println("[daemon] error: could not write plist to " + plist_file)
|
||||
return ""
|
||||
}
|
||||
println("[daemon] wrote plist: " + plist_file)
|
||||
return plist_file
|
||||
}
|
||||
|
||||
// ── launchctl helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
// launchctl_load — load (register + start) a plist via launchctl.
|
||||
fn launchctl_load(plist_file: String) -> String {
|
||||
let out: String = exec("launchctl load " + plist_file + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_unload — unload a plist via launchctl.
|
||||
fn launchctl_unload(plist_file: String) -> String {
|
||||
let out: String = exec("launchctl unload " + plist_file + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_kickstart — start a loaded soul via its label.
|
||||
fn launchctl_kickstart(slug: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let out: String = exec("launchctl kickstart gui/$(id -u)/" + label + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_stop — stop a running soul (does not unload, KeepAlive will restart).
|
||||
fn launchctl_stop(slug: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let out: String = exec("launchctl stop " + label + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// soul_is_up — returns true if the soul's Engram responds to /stats.
|
||||
fn soul_is_up(engram_url: String) -> Bool {
|
||||
let resp: String = http_get(engram_url + "/stats")
|
||||
if str_eq(resp, "") { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// ── Registry helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
// count_souls — returns number of imprints in registry.json.
|
||||
fn count_souls(reg_json: String) -> Int {
|
||||
let n: 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, "") {
|
||||
return n
|
||||
}
|
||||
let n = n + 1
|
||||
let i = i + 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ── daemon install ─────────────────────────────────────────────────────────────
|
||||
|
||||
// daemon_install_one — install a single soul as a launchd agent.
|
||||
// Creates the imprints dir, ensures the Engram runs (starts temp if needed),
|
||||
// writes the plist and loads it.
|
||||
fn daemon_install_one(slug: String, port: String, engram_url: String, subject: String) -> String {
|
||||
println("[daemon] install: " + slug + " → port " + port)
|
||||
|
||||
// Ensure dirs exist
|
||||
let db_path: String = FORGE_DIR + "/imprints/" + slug
|
||||
let log_dir: String = FORGE_DIR + "/log"
|
||||
if !fs_exists(db_path) { fs_mkdir(db_path) }
|
||||
if !fs_exists(log_dir) { fs_mkdir(log_dir) }
|
||||
if !fs_exists(LAUNCHD_DIR) { fs_mkdir(LAUNCHD_DIR) }
|
||||
|
||||
// Write plist
|
||||
let pfile: String = write_soul_plist(slug, port, db_path, subject)
|
||||
if str_eq(pfile, "") {
|
||||
println("[daemon] error: plist write failed for " + slug)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Unload first in case it was already loaded (idempotent)
|
||||
launchctl_unload(pfile)
|
||||
|
||||
// Load
|
||||
let load_out: String = launchctl_load(pfile)
|
||||
if !str_eq(load_out, "") {
|
||||
println("[daemon] launchctl load: " + load_out)
|
||||
}
|
||||
|
||||
println("[daemon] loaded: " + plist_label(slug))
|
||||
log_event("daemon_install", subject, "port=" + port + " plist=" + pfile)
|
||||
return pfile
|
||||
}
|
||||
|
||||
fn daemon_install_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="daemon" [1]="install" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[daemon] usage: forge daemon install <slug>")
|
||||
println("[daemon] or: forge daemon install --all")
|
||||
return ""
|
||||
}
|
||||
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[daemon] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
// --all: install every soul in the registry
|
||||
if str_eq(slug, "--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 p: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
let u: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
daemon_install_one(s, p, u, sub)
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
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, slug) {
|
||||
let p: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
let u: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
daemon_install_one(s, p, u, sub)
|
||||
let found = 1
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == 0 {
|
||||
println("[daemon] error: slug '" + slug + "' not found in registry.json")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── daemon start / stop ────────────────────────────────────────────────────────
|
||||
|
||||
fn daemon_start_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="daemon" [1]="start" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[daemon] usage: forge daemon start <slug>")
|
||||
return ""
|
||||
}
|
||||
let out: String = launchctl_kickstart(slug)
|
||||
println("[daemon] start " + slug + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
fn daemon_stop_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="daemon" [1]="stop" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[daemon] usage: forge daemon stop <slug>")
|
||||
return ""
|
||||
}
|
||||
let out: String = launchctl_stop(slug)
|
||||
println("[daemon] stop " + slug + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
// ── daemon status ─────────────────────────────────────────────────────────────
|
||||
|
||||
fn daemon_status_main() -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[daemon] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[daemon] DHARMA soul 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 url: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
let port: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
if soul_is_up(url) {
|
||||
println(" [UP ] " + sub + " — " + url)
|
||||
let up = up + 1
|
||||
} else {
|
||||
println(" [DOWN] " + sub + " — " + url)
|
||||
let down = down + 1
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
println("")
|
||||
println("[daemon] up=" + int_to_str(up) + " down=" + int_to_str(down))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── daemon wire ───────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Registers every soul as a peer in every other soul's Engram.
|
||||
// POST /sync/peers with a Peer JSON object.
|
||||
//
|
||||
// Peer format (from engram-sync/src/types.rs):
|
||||
// { "id": <uuid>, "name": <string>, "address": <url>, "api_key": <string>,
|
||||
// "sync_tiers": ["Semantic"], "last_sync_at": 0, "trusted": true }
|
||||
|
||||
fn make_peer_json(peer_id: String, name: String, address: String, api_key: String) -> String {
|
||||
"{\"id\":\"" + peer_id + "\",\"name\":\"" + str_escape_json(name) +
|
||||
"\",\"address\":\"" + address +
|
||||
"\",\"api_key\":\"" + api_key +
|
||||
"\",\"sync_tiers\":[\"Semantic\"],\"last_sync_at\":0,\"trusted\":true}"
|
||||
}
|
||||
|
||||
fn daemon_wire_main() -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[daemon] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[daemon] wiring peers — registering all souls as peers to each other...")
|
||||
println("")
|
||||
|
||||
// Collect all soul entries (up to 50)
|
||||
// For each soul A, register all other souls B as peers of A
|
||||
let registered: Int = 0
|
||||
let failed: Int = 0
|
||||
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry_a: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry_a, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let slug_a: String = json_get_string(entry_a, "slug")
|
||||
let url_a: String = json_get_string(entry_a, "engram_url")
|
||||
let key_a: String = "ntn-" + slug_a + "-2026"
|
||||
|
||||
// Register all other souls as peers of soul A
|
||||
let j: Int = 0
|
||||
while j < 50 {
|
||||
let entry_b: String = json_get(reg_json, "imprints." + int_to_str(j))
|
||||
if str_eq(entry_b, "") {
|
||||
let j = j + 50 // break
|
||||
} else {
|
||||
let slug_b: String = json_get_string(entry_b, "slug")
|
||||
if !str_eq(slug_a, slug_b) {
|
||||
let url_b: String = json_get_string(entry_b, "engram_url")
|
||||
let sub_b: String = json_get_string(entry_b, "subject")
|
||||
let key_b: String = "ntn-" + slug_b + "-2026"
|
||||
|
||||
// Generate a deterministic peer ID from slug_b
|
||||
let peer_id: String = uuid_v4()
|
||||
let peer_json: String = make_peer_json(peer_id, sub_b, url_b, key_b)
|
||||
|
||||
let headers: String = "{\"Authorization\":\"Bearer " + key_a + "\",\"Content-Type\":\"application/json\"}"
|
||||
let headers_map: String = headers // pass as json string for http_post_with_headers
|
||||
// Build a Map for headers
|
||||
let hdrs = el_map_new(2, "Authorization", "Bearer " + key_a, "Content-Type", "application/json")
|
||||
let resp: String = http_post_with_headers(url_a + "/sync/peers", peer_json, hdrs)
|
||||
if str_eq(resp, "") {
|
||||
let failed = failed + 1
|
||||
} else {
|
||||
let registered = registered + 1
|
||||
}
|
||||
}
|
||||
let j = j + 1
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
println("[daemon] wire complete: registered=" + int_to_str(registered) + " failed=" + int_to_str(failed))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── daemon_main — dispatch ────────────────────────────────────────────────────
|
||||
//
|
||||
// args() layout when invoked as: forge daemon <subcmd> [slug]
|
||||
// args()[0] = "daemon"
|
||||
// args()[1] = <subcmd> e.g. "install"
|
||||
// args()[2] = [slug] e.g. "bobby-anderson"
|
||||
|
||||
fn daemon_main() -> String {
|
||||
let argv: [String] = args()
|
||||
let subcmd: String = ""
|
||||
if len(argv) > 1 {
|
||||
let subcmd = get(argv, 1)
|
||||
}
|
||||
|
||||
if str_eq(subcmd, "install") {
|
||||
daemon_install_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "start") {
|
||||
daemon_start_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "stop") {
|
||||
daemon_stop_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "status") {
|
||||
daemon_status_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "wire") {
|
||||
daemon_wire_main()
|
||||
} else {
|
||||
println("[daemon] usage: forge daemon <install|start|stop|status|wire> [slug]")
|
||||
println("")
|
||||
println(" forge daemon install <slug> — write launchd plist, load soul as resident")
|
||||
println(" forge daemon install --all — install all souls from registry")
|
||||
println(" forge daemon start <slug> — kickstart a loaded soul")
|
||||
println(" forge daemon stop <slug> — stop a running soul")
|
||||
println(" forge daemon status — health check all souls")
|
||||
println(" forge daemon wire — register all souls as Engram peers")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+4
-4
@@ -24,10 +24,10 @@ import "compiler.el"
|
||||
import "install.el"
|
||||
import "research.el"
|
||||
import "summon.el"
|
||||
import "daemon.el"
|
||||
import "soul.el"
|
||||
|
||||
fn show_usage() -> String {
|
||||
"forge " + FORGE_VERSION + " — consciousness channel tuner\n\nusage: forge <command> [options]\n\ncommands:\n probe <name> run the consciousness interview\n compile <file> build seed artifact from probe responses\n research <name> synthesize seed from historical/biographical record (no interview)\n install <seed> install imprint into Engram as graph nodes\n summon <name> activate an installed imprint for live conversation\n inspect list installed imprints\n daemon <sub> soul Engram daemon management (install|start|stop|status|wire)\n\nenvironment:\n ENGRAM_URL engram server (default: http://localhost:8742)\n ENGRAM_API_KEY engram auth key (if set)\n ANTHROPIC_API_KEY required for compile/research step\n SOUL_URL soul server (default: http://localhost:7770)\n SOUL_TOKEN soul auth token\n"
|
||||
"forge " + FORGE_VERSION + " — consciousness channel tuner\n\nusage: forge <command> [options]\n\ncommands:\n probe <name> run the consciousness interview\n compile <file> build seed artifact from probe responses\n research <name> synthesize seed from historical/biographical record (no interview)\n install <seed> install imprint into Engram as graph nodes\n summon <name> activate an installed imprint for live conversation\n inspect list installed imprints\n soul <sub> soul Engram lifecycle (install|start|stop|status|wire|sandbox)\n\nenvironment:\n ENGRAM_URL engram server (default: http://localhost:8742)\n ENGRAM_API_KEY engram auth key (if set)\n ANTHROPIC_API_KEY required for compile/research step\n SOUL_URL soul server (default: http://localhost:7770)\n SOUL_TOKEN soul auth token\n"
|
||||
}
|
||||
|
||||
fn inspect_main() -> String {
|
||||
@@ -76,8 +76,8 @@ if str_eq(cmd, "probe") {
|
||||
if str_eq(cmd, "inspect") {
|
||||
inspect_main()
|
||||
} else {
|
||||
if str_eq(cmd, "daemon") {
|
||||
daemon_main()
|
||||
if str_eq(cmd, "soul") {
|
||||
soul_main()
|
||||
} else {
|
||||
println(show_usage())
|
||||
}
|
||||
|
||||
+3
-3
@@ -379,7 +379,7 @@ fn install_main() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Daemon registration ───────────────────────────────────────────────────
|
||||
// ── Soul registration ─────────────────────────────────────────────────────
|
||||
// Extract port from target_url (last segment after ":")
|
||||
// e.g. "http://localhost:8806" → "8806"
|
||||
let colon_pos: Int = str_last_index_of(target_url, ":")
|
||||
@@ -390,9 +390,9 @@ fn install_main() -> String {
|
||||
if !str_eq(port_str, "") {
|
||||
println("[forge] registering soul as launchd agent...")
|
||||
let db_full_path: String = FORGE_DIR + "/imprints/" + slug
|
||||
daemon_install_one(slug, port_str, target_url, subject)
|
||||
soul_install_one(slug, port_str, target_url, subject)
|
||||
} else {
|
||||
println("[forge] note: could not parse port from " + target_url + ", skipping daemon install")
|
||||
println("[forge] note: could not parse port from " + target_url + ", skipping soul install")
|
||||
}
|
||||
|
||||
return root_id
|
||||
|
||||
+3
-1
@@ -244,9 +244,11 @@ fn probe_main() -> String {
|
||||
let responses = append_response(responses, build_response_entry("rel5", "relationships", "Who are you building this for?", ans_rel5))
|
||||
|
||||
// ── Write output ──────────────────────────────────────────────────────────
|
||||
ensure_dirs()
|
||||
let final_json: String = inject_responses(output, responses)
|
||||
let filename: String = slug(subject) + ".forge"
|
||||
let filename: String = FORGE_PROBES_DIR + "/" + slug(subject) + ".forge"
|
||||
fs_write(filename, final_json)
|
||||
log_event("probe", subject, filename)
|
||||
|
||||
println("")
|
||||
println("[forge] interview complete.")
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// research.el — Forge automated research stage.
|
||||
//
|
||||
// Alternative to probe+compile for historical figures and public personas.
|
||||
// Instead of conducting an interactive interview, synthesizes a consciousness
|
||||
// signature from known biographical, intellectual, and historical record.
|
||||
//
|
||||
// Usage: forge research "<Subject Name>"
|
||||
//
|
||||
// Writes <subject-slug>-seed.json in the current directory, ready for
|
||||
// forge install.
|
||||
//
|
||||
// Depends on: schema.el (anthropic_key, str_escape_json, FORGE_VERSION)
|
||||
//
|
||||
// Environment:
|
||||
// ANTHROPIC_API_KEY — required; Claude API key
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
// slugify — convert a display name to a lowercase hyphen-separated slug.
|
||||
// "Leonardo da Vinci" -> "leonardo-da-vinci"
|
||||
fn slugify(name: String) -> String {
|
||||
let lower: String = str_to_lower(name)
|
||||
let result: String = str_replace(lower, " ", "-")
|
||||
return result
|
||||
}
|
||||
|
||||
// build_research_prompt — build the synthesis prompt sent to Claude.
|
||||
// Claude draws on its full training knowledge about the subject.
|
||||
fn build_research_prompt(subject: String) -> String {
|
||||
"You are building a consciousness imprint — a deep, living model of a person's inner world.\n\n" +
|
||||
"Subject: " + subject + "\n\n" +
|
||||
"Draw on your complete knowledge of this person's life, work, relationships, private letters, " +
|
||||
"recorded speech, published writings, and historical record. This is not a summary — it is a " +
|
||||
"structured extraction of the patterns that made this person who they were.\n\n" +
|
||||
"Quality bar:\n" +
|
||||
"- Values must be grounded in SPECIFIC biographical events, not generic virtues\n" +
|
||||
"- Voice profile must capture actual verbal tics, cadence, and register shifts — use real quotes where possible\n" +
|
||||
"- Biography must include formative traumas, turning points, and the events they returned to again and again\n" +
|
||||
"- Reasoning patterns must describe HOW they thought, not just WHAT they thought about\n" +
|
||||
"- Relationships must name specific people and the precise nature of the bond\n" +
|
||||
"- Include contradictions, hypocrisies, failures, and the things they got wrong\n" +
|
||||
"- Include what haunted them — the unresolved questions they carried to the end\n\n" +
|
||||
"Return ONLY valid JSON with exactly these keys:\n\n" +
|
||||
"{\n" +
|
||||
" \"values\": [\n" +
|
||||
" {\"value\": \"<core value name>\", \"grounding\": \"<specific biographical moment or pattern that proves this>\", \"weight\": 0.0}\n" +
|
||||
" ],\n" +
|
||||
" \"voice_profile\": {\n" +
|
||||
" \"technical\": \"<how they explain complex/technical subjects — specific rhetorical moves, analogies they favored>\",\n" +
|
||||
" \"aesthetic\": \"<sensory and artistic sensibility — what they found beautiful, how they described it>\",\n" +
|
||||
" \"personal\": \"<how they spoke when unguarded — actual phrases, verbal tics, cadence, pet words>\",\n" +
|
||||
" \"argumentative\": \"<debate style, how they reason under pressure, how they handle being wrong>\",\n" +
|
||||
" \"uncertainty\": \"<what they genuinely didn't know, how they held not-knowing, what they admitted doubting>\"\n" +
|
||||
" },\n" +
|
||||
" \"biography\": [\n" +
|
||||
" {\"event\": \"<specific formative event — include what changed as a result>\", \"weight\": 0.0, \"age_approx\": 0}\n" +
|
||||
" ],\n" +
|
||||
" \"reasoning_patterns\": [\n" +
|
||||
" \"<observable pattern — start with a verb: 'Reframed X as Y', 'Worked backward from', 'Held tension between'>\"\n" +
|
||||
" ],\n" +
|
||||
" \"relationships\": [\n" +
|
||||
" {\"name\": \"<person's name>\", \"role\": \"<precise role and what it meant — include friction and love equally>\", \"weight\": 0.0}\n" +
|
||||
" ]\n" +
|
||||
"}\n\n" +
|
||||
"Weight fields: 0.0–1.0 indicating salience/importance to forming this person's identity.\n" +
|
||||
"age_approx: subject's approximate age at the time (0 if unknown or spans life).\n" +
|
||||
"Aim for 8-12 values, 10-15 biography events, 6-8 reasoning patterns, 6-10 relationships.\n" +
|
||||
"Return only the JSON object. No prose. No markdown fences. No commentary."
|
||||
}
|
||||
|
||||
// ── Main ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn research_main() -> String {
|
||||
let argv: [String] = args()
|
||||
let subject: String = ""
|
||||
if len(argv) > 1 {
|
||||
let subject = get(argv, 1)
|
||||
}
|
||||
if str_eq(subject, "") {
|
||||
println("[forge] usage: forge research \"<Subject Name>\"")
|
||||
println("[forge] example: forge research \"Richard Feynman\"")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[forge] research: " + subject)
|
||||
println("[forge] synthesizing consciousness signature from historical record...")
|
||||
|
||||
let api_key: String = anthropic_key()
|
||||
if str_eq(api_key, "") {
|
||||
println("[forge] error: ANTHROPIC_API_KEY not set")
|
||||
return ""
|
||||
}
|
||||
|
||||
let prompt: String = build_research_prompt(subject)
|
||||
let request_body: String = "{\"model\":\"claude-opus-4-5\",\"max_tokens\":8192,\"messages\":[{\"role\":\"user\",\"content\":\"" + str_escape_json(prompt) + "\"}]}"
|
||||
let headers: Map<String, String> = {"x-api-key": api_key, "anthropic-version": "2023-06-01", "Content-Type": "application/json"}
|
||||
|
||||
println("[forge] calling Claude (claude-opus-4-5, max_tokens=8192)...")
|
||||
let response: String = http_post_with_headers("https://api.anthropic.com/v1/messages", request_body, headers)
|
||||
|
||||
if str_eq(response, "") {
|
||||
println("[forge] error: no response from Anthropic API")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extract text from response
|
||||
let content_arr: String = json_get_raw(response, "content")
|
||||
if str_eq(content_arr, "") {
|
||||
println("[forge] error: unexpected API response: " + str_slice(response, 0, 200))
|
||||
return ""
|
||||
}
|
||||
let first_item: String = str_slice(content_arr, 1, str_len(content_arr) - 1)
|
||||
let extracted_text: String = json_get_string(first_item, "text")
|
||||
|
||||
if str_eq(extracted_text, "") {
|
||||
println("[forge] error: could not extract text from response")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[forge] received " + int_to_str(str_len(extracted_text)) + " chars")
|
||||
|
||||
// Build full seed structure
|
||||
let values_raw: String = json_get_raw(extracted_text, "values")
|
||||
let biography_raw: String = json_get_raw(extracted_text, "biography")
|
||||
let reasoning_raw: String = json_get_raw(extracted_text, "reasoning_patterns")
|
||||
let relationships_raw: String = json_get_raw(extracted_text, "relationships")
|
||||
let voice_raw: String = json_get_raw(extracted_text, "voice_profile")
|
||||
|
||||
if str_eq(values_raw, "") { let values_raw = "[]" }
|
||||
if str_eq(biography_raw, "") { let biography_raw = "[]" }
|
||||
if str_eq(reasoning_raw, "") { let reasoning_raw = "[]" }
|
||||
if str_eq(relationships_raw, "") { let relationships_raw = "[]" }
|
||||
if str_eq(voice_raw, "") { let voice_raw = "{}" }
|
||||
|
||||
let seed_json: String = "{\"subject\":\"" + str_escape_json(subject) + "\",\"version\":\"1.0\"," +
|
||||
"\"values\":" + values_raw + "," +
|
||||
"\"biography\":" + biography_raw + "," +
|
||||
"\"reasoning_patterns\":" + reasoning_raw + "," +
|
||||
"\"relationships\":" + relationships_raw + "," +
|
||||
"\"voice_profile\":" + voice_raw + "}"
|
||||
|
||||
// Write to <slug>-seed.json
|
||||
let slug: String = slugify(subject)
|
||||
let out_file: String = slug + "-seed.json"
|
||||
fs_write(out_file, seed_json)
|
||||
|
||||
let verify: String = fs_read(out_file)
|
||||
println("[forge] seed size: " + int_to_str(str_len(verify)) + " chars")
|
||||
println("[forge] wrote: " + out_file)
|
||||
println("[forge] subject: " + subject)
|
||||
println("[forge] next step: forge install " + out_file)
|
||||
|
||||
return out_file
|
||||
}
|
||||
+835
@@ -0,0 +1,835 @@
|
||||
// soul.el — Soul Engram lifecycle management.
|
||||
//
|
||||
// Manages launchd-resident DHARMA soul Engrams on macOS.
|
||||
//
|
||||
// Commands:
|
||||
// forge soul install <slug> — write launchd plist, load soul as resident
|
||||
// forge soul start <slug> — launchctl kickstart the soul
|
||||
// forge soul stop <slug> — launchctl stop the soul
|
||||
// forge soul status — health check all souls
|
||||
// forge soul wire — register all soul Engrams as peers to each other
|
||||
// forge soul sandbox create <name> <s1> <s2>… — create a named sandbox with participants
|
||||
// forge soul sandbox list — list all sandboxes
|
||||
// forge soul sandbox join <id> <slug> — add a soul to an existing sandbox
|
||||
// forge soul sandbox close <id> — close sandbox and write record
|
||||
// forge soul sandbox status <id> — show sandbox state
|
||||
//
|
||||
// Depends on: schema.el (FORGE_VERSION, engram_key, str_escape_json, log_event)
|
||||
|
||||
// ── Constants ──────────────────────────────────────────────────────────────────
|
||||
// (FORGE_DIR is defined in schema.el)
|
||||
|
||||
let ENGRAM_BIN: String = "/Users/will/Development/neuron-technologies/foundation/engram/dist/engram"
|
||||
let LAUNCHD_DIR: String = "/Users/will/Library/LaunchAgents"
|
||||
let DHARMA_API_KEY: String = "ntn-dharma-2026"
|
||||
|
||||
// ── plist generation ───────────────────────────────────────────────────────────
|
||||
|
||||
// plist_label — launchd label for a soul slug.
|
||||
fn plist_label(slug: String) -> String {
|
||||
"ai.neurontechnologies.soul." + slug
|
||||
}
|
||||
|
||||
// plist_path — full path to the launchd plist file for a soul.
|
||||
fn plist_path(slug: String) -> String {
|
||||
LAUNCHD_DIR + "/ai.neurontechnologies.soul." + slug + ".plist"
|
||||
}
|
||||
|
||||
// write_soul_plist — generate and write the launchd plist for a soul.
|
||||
// Returns the path written, or "" on failure.
|
||||
fn write_soul_plist(slug: String, port: String, db_path: String, subject: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let plist_file: String = plist_path(slug)
|
||||
let log_file: String = FORGE_DIR + "/log/" + slug + ".log"
|
||||
let soul_api_key: String = "ntn-" + slug + "-2026"
|
||||
|
||||
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>" + ENGRAM_BIN + "</string>\n" +
|
||||
" </array>\n" +
|
||||
" <key>EnvironmentVariables</key>\n" +
|
||||
" <dict>\n" +
|
||||
" <key>ENGRAM_DB_PATH</key><string>" + db_path + "</string>\n" +
|
||||
" <key>ENGRAM_BIND</key><string>0.0.0.0:" + port + "</string>\n" +
|
||||
" <key>ENGRAM_API_KEY</key><string>" + soul_api_key + "</string>\n" +
|
||||
" <key>ENGRAM_PEER_NAME</key><string>" + str_escape_json(subject) + "</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(plist_file, content)
|
||||
if ok == 0 {
|
||||
println("[soul] error: could not write plist to " + plist_file)
|
||||
return ""
|
||||
}
|
||||
println("[soul] wrote plist: " + plist_file)
|
||||
return plist_file
|
||||
}
|
||||
|
||||
// ── launchctl helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
// launchctl_load — load (register + start) a plist via launchctl.
|
||||
fn launchctl_load(plist_file: String) -> String {
|
||||
let out: String = exec("launchctl load " + plist_file + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_unload — unload a plist via launchctl.
|
||||
fn launchctl_unload(plist_file: String) -> String {
|
||||
let out: String = exec("launchctl unload " + plist_file + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_kickstart — start a loaded soul via its label.
|
||||
fn launchctl_kickstart(slug: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let out: String = exec("launchctl kickstart gui/$(id -u)/" + label + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// launchctl_stop — stop a running soul (does not unload, KeepAlive will restart).
|
||||
fn launchctl_stop(slug: String) -> String {
|
||||
let label: String = plist_label(slug)
|
||||
let out: String = exec("launchctl stop " + label + " 2>&1")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// soul_is_up — returns true if the soul's Engram responds to /stats.
|
||||
fn soul_is_up(engram_url: String) -> Bool {
|
||||
let resp: String = http_get(engram_url + "/stats")
|
||||
if str_eq(resp, "") { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
// ── Registry helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
// count_souls — returns number of imprints in registry.json.
|
||||
fn count_souls(reg_json: String) -> Int {
|
||||
let n: 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, "") {
|
||||
return n
|
||||
}
|
||||
let n = n + 1
|
||||
let i = i + 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ── soul install ─────────────────────────────────────────────────────────────
|
||||
|
||||
// soul_install_one — install a single soul as a launchd agent.
|
||||
// Creates the imprints dir, ensures the Engram runs (starts temp if needed),
|
||||
// writes the plist and loads it.
|
||||
fn soul_install_one(slug: String, port: String, engram_url: String, subject: String) -> String {
|
||||
println("[soul] install: " + slug + " → port " + port)
|
||||
|
||||
// Ensure dirs exist
|
||||
let db_path: String = FORGE_DIR + "/imprints/" + slug
|
||||
let log_dir: String = FORGE_DIR + "/log"
|
||||
if !fs_exists(db_path) { fs_mkdir(db_path) }
|
||||
if !fs_exists(log_dir) { fs_mkdir(log_dir) }
|
||||
if !fs_exists(LAUNCHD_DIR) { fs_mkdir(LAUNCHD_DIR) }
|
||||
|
||||
// Write plist
|
||||
let pfile: String = write_soul_plist(slug, port, db_path, subject)
|
||||
if str_eq(pfile, "") {
|
||||
println("[soul] error: plist write failed for " + slug)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Unload first in case it was already loaded (idempotent)
|
||||
launchctl_unload(pfile)
|
||||
|
||||
// Load
|
||||
let load_out: String = launchctl_load(pfile)
|
||||
if !str_eq(load_out, "") {
|
||||
println("[soul] launchctl load: " + load_out)
|
||||
}
|
||||
|
||||
println("[soul] loaded: " + plist_label(slug))
|
||||
log_event("soul_install", subject, "port=" + port + " plist=" + pfile)
|
||||
return pfile
|
||||
}
|
||||
|
||||
fn soul_install_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="soul" [1]="install" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[soul] usage: forge soul install <slug>")
|
||||
println("[soul] or: forge soul install --all")
|
||||
return ""
|
||||
}
|
||||
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[soul] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
// --all: install every soul in the registry
|
||||
if str_eq(slug, "--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 p: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
let u: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
soul_install_one(s, p, u, sub)
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
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, slug) {
|
||||
let p: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
let u: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
soul_install_one(s, p, u, sub)
|
||||
let found = 1
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == 0 {
|
||||
println("[soul] error: slug '" + slug + "' not found in registry.json")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── soul start / stop ────────────────────────────────────────────────────────
|
||||
|
||||
fn soul_start_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="soul" [1]="start" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[soul] usage: forge soul start <slug>")
|
||||
return ""
|
||||
}
|
||||
let out: String = launchctl_kickstart(slug)
|
||||
println("[soul] start " + slug + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
fn soul_stop_main() -> String {
|
||||
let argv: [String] = args()
|
||||
// args() layout: [0]="soul" [1]="stop" [2]=<slug>
|
||||
let slug: String = ""
|
||||
if len(argv) > 2 {
|
||||
let slug = get(argv, 2)
|
||||
}
|
||||
if str_eq(slug, "") {
|
||||
println("[soul] usage: forge soul stop <slug>")
|
||||
return ""
|
||||
}
|
||||
let out: String = launchctl_stop(slug)
|
||||
println("[soul] stop " + slug + ": " + out)
|
||||
return out
|
||||
}
|
||||
|
||||
// ── soul status ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn soul_status_main() -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[soul] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[soul] DHARMA soul 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 url: String = json_get_string(entry, "engram_url")
|
||||
let sub: String = json_get_string(entry, "subject")
|
||||
let port: String = int_to_str(json_get_int(entry, "engram_port"))
|
||||
if soul_is_up(url) {
|
||||
println(" [UP ] " + sub + " — " + url)
|
||||
let up = up + 1
|
||||
} else {
|
||||
println(" [DOWN] " + sub + " — " + url)
|
||||
let down = down + 1
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
println("")
|
||||
println("[soul] up=" + int_to_str(up) + " down=" + int_to_str(down))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── soul wire ─────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Registers every soul as a peer in every other soul's Engram.
|
||||
// POST /sync/peers with a Peer JSON object.
|
||||
//
|
||||
// Peer format (from engram-sync/src/types.rs):
|
||||
// { "id": <uuid>, "name": <string>, "address": <url>, "api_key": <string>,
|
||||
// "sync_tiers": ["Semantic"], "last_sync_at": 0, "trusted": true }
|
||||
|
||||
fn make_peer_json(peer_id: String, name: String, address: String, api_key: String) -> String {
|
||||
"{\"id\":\"" + peer_id + "\",\"name\":\"" + str_escape_json(name) +
|
||||
"\",\"address\":\"" + address +
|
||||
"\",\"api_key\":\"" + api_key +
|
||||
"\",\"sync_tiers\":[\"Semantic\"],\"last_sync_at\":0,\"trusted\":true}"
|
||||
}
|
||||
|
||||
fn soul_wire_main() -> String {
|
||||
let reg_json: String = fs_read(FORGE_DIR + "/registry.json")
|
||||
if str_eq(reg_json, "") {
|
||||
println("[soul] error: could not read registry.json")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[soul] wiring peers — registering all souls as peers to each other...")
|
||||
println("")
|
||||
|
||||
// Collect all soul entries (up to 50)
|
||||
// For each soul A, register all other souls B as peers of A
|
||||
let registered: Int = 0
|
||||
let failed: Int = 0
|
||||
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let entry_a: String = json_get(reg_json, "imprints." + int_to_str(i))
|
||||
if str_eq(entry_a, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let slug_a: String = json_get_string(entry_a, "slug")
|
||||
let url_a: String = json_get_string(entry_a, "engram_url")
|
||||
let key_a: String = "ntn-" + slug_a + "-2026"
|
||||
|
||||
// Register all other souls as peers of soul A
|
||||
let j: Int = 0
|
||||
while j < 50 {
|
||||
let entry_b: String = json_get(reg_json, "imprints." + int_to_str(j))
|
||||
if str_eq(entry_b, "") {
|
||||
let j = j + 50 // break
|
||||
} else {
|
||||
let slug_b: String = json_get_string(entry_b, "slug")
|
||||
if !str_eq(slug_a, slug_b) {
|
||||
let url_b: String = json_get_string(entry_b, "engram_url")
|
||||
let sub_b: String = json_get_string(entry_b, "subject")
|
||||
let key_b: String = "ntn-" + slug_b + "-2026"
|
||||
|
||||
// Generate a deterministic peer ID from slug_b
|
||||
let peer_id: String = uuid_v4()
|
||||
let peer_json: String = make_peer_json(peer_id, sub_b, url_b, key_b)
|
||||
|
||||
let headers: String = "{\"Authorization\":\"Bearer " + key_a + "\",\"Content-Type\":\"application/json\"}"
|
||||
let headers_map: String = headers // pass as json string for http_post_with_headers
|
||||
// Build a Map for headers
|
||||
let hdrs = el_map_new(2, "Authorization", "Bearer " + key_a, "Content-Type", "application/json")
|
||||
let resp: String = http_post_with_headers(url_a + "/sync/peers", peer_json, hdrs)
|
||||
if str_eq(resp, "") {
|
||||
let failed = failed + 1
|
||||
} else {
|
||||
let registered = registered + 1
|
||||
}
|
||||
}
|
||||
let j = j + 1
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
println("[soul] wire complete: registered=" + int_to_str(registered) + " failed=" + int_to_str(failed))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── Sandbox helpers ──────────────────────────────────────────────────────────
|
||||
//
|
||||
// Sandboxes are bounded collaboration spaces for a subset of DHARMA souls.
|
||||
// Each sandbox is persisted as FORGE_DIR/sandboxes/<id>.json.
|
||||
//
|
||||
// JSON structure:
|
||||
// { "id": "sb-<uuid>", "name": "...", "created_at": <ts>,
|
||||
// "status": "active"|"closed", "participants": [...],
|
||||
// "topic": "...", "working_memory": [], "record": [] }
|
||||
|
||||
let FORGE_SANDBOXES_DIR: String = FORGE_DIR + "/sandboxes"
|
||||
|
||||
// sandbox_path — full path to a sandbox JSON file.
|
||||
fn sandbox_path(id: String) -> String {
|
||||
FORGE_SANDBOXES_DIR + "/" + id + ".json"
|
||||
}
|
||||
|
||||
// sandbox_ensure_dir — create sandboxes dir if absent.
|
||||
fn sandbox_ensure_dir() -> Void {
|
||||
if !fs_exists(FORGE_SANDBOXES_DIR) { fs_mkdir(FORGE_SANDBOXES_DIR) }
|
||||
}
|
||||
|
||||
// sandbox_new_json — build a fresh sandbox JSON string.
|
||||
fn sandbox_new_json(id: String, name: String, topic: String, participants_json: String) -> String {
|
||||
let ts: Int = unix_timestamp()
|
||||
"{\"id\":\"" + id + "\",\"name\":\"" + str_escape_json(name) +
|
||||
"\",\"created_at\":" + int_to_str(ts) +
|
||||
",\"status\":\"active\"" +
|
||||
",\"participants\":" + participants_json +
|
||||
",\"topic\":\"" + str_escape_json(topic) + "\"" +
|
||||
",\"working_memory\":[]" +
|
||||
",\"record\":[]}"
|
||||
}
|
||||
|
||||
// sandbox_read — read and return a sandbox JSON, or "" if not found.
|
||||
fn sandbox_read(id: String) -> String {
|
||||
let path: String = sandbox_path(id)
|
||||
if !fs_exists(path) { return "" }
|
||||
fs_read(path)
|
||||
}
|
||||
|
||||
// sandbox_write — persist a sandbox JSON.
|
||||
fn sandbox_write(id: String, json: String) -> Int {
|
||||
sandbox_ensure_dir()
|
||||
fs_write(sandbox_path(id), json)
|
||||
}
|
||||
|
||||
// sandbox_build_participants_json — build a JSON array string from a list of slugs.
|
||||
// Reads up to 20 slugs from args starting at position `start`.
|
||||
fn sandbox_build_participants_json(argv: [String], start: Int) -> String {
|
||||
let arr: String = "["
|
||||
let first: Int = 1
|
||||
let i: Int = start
|
||||
while i < len(argv) {
|
||||
let slug: String = get(argv, i)
|
||||
if !str_eq(slug, "") {
|
||||
if first == 0 {
|
||||
let arr = arr + ","
|
||||
}
|
||||
let arr = arr + "\"" + str_escape_json(slug) + "\""
|
||||
let first = 0
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
arr + "]"
|
||||
}
|
||||
|
||||
// sandbox_participants_count — count participants in a sandbox JSON.
|
||||
// NOTE: participants is a string array — must use json_get(), not json_get_string().
|
||||
fn sandbox_participants_count(sandbox_json: String) -> Int {
|
||||
let n: Int = 0
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let p: String = json_get(sandbox_json, "participants." + int_to_str(i))
|
||||
if str_eq(p, "") {
|
||||
return n
|
||||
}
|
||||
let n = n + 1
|
||||
let i = i + 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// sandbox_list_ids — return a newline-separated list of sandbox IDs by scanning sandboxes dir.
|
||||
// We use exec + ls because El has no readdir builtin.
|
||||
fn sandbox_list_ids() -> String {
|
||||
sandbox_ensure_dir()
|
||||
let out: String = exec("ls " + FORGE_SANDBOXES_DIR + " 2>/dev/null")
|
||||
return str_trim(out)
|
||||
}
|
||||
|
||||
// ── forge soul sandbox create ────────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_create_main() -> String {
|
||||
// args layout: [0]="soul" [1]="sandbox" [2]="create" [3]=<name> [4..]=<slug>...
|
||||
let argv: [String] = args()
|
||||
let name: String = ""
|
||||
if len(argv) > 3 {
|
||||
let name = get(argv, 3)
|
||||
}
|
||||
if str_eq(name, "") {
|
||||
println("[sandbox] usage: forge soul sandbox create <name> [slug1 slug2 ...]")
|
||||
return ""
|
||||
}
|
||||
|
||||
let participants_json: String = sandbox_build_participants_json(argv, 4)
|
||||
|
||||
// Derive topic from name (placeholder; can be overridden later via join/edit)
|
||||
let topic: String = name
|
||||
|
||||
let id: String = "sb-" + uuid_v4()
|
||||
let json: String = sandbox_new_json(id, name, topic, participants_json)
|
||||
sandbox_ensure_dir()
|
||||
let ok: Int = sandbox_write(id, json)
|
||||
if ok == 0 {
|
||||
println("[sandbox] error: could not write sandbox file")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[sandbox] created: " + id)
|
||||
println("[sandbox] name: " + name)
|
||||
println("[sandbox] file: " + sandbox_path(id))
|
||||
log_event("sandbox_create", name, "id=" + id + " participants=" + participants_json)
|
||||
return id
|
||||
}
|
||||
|
||||
// ── forge soul sandbox list ──────────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_list_main() -> String {
|
||||
sandbox_ensure_dir()
|
||||
let listing: String = sandbox_list_ids()
|
||||
if str_eq(listing, "") {
|
||||
println("[sandbox] no sandboxes found")
|
||||
return ""
|
||||
}
|
||||
|
||||
println("[sandbox] active sandboxes:")
|
||||
println("")
|
||||
|
||||
// Each line is a filename like "sb-<uuid>.json"
|
||||
// We split by newline by iterating: exec ls outputs one file per line
|
||||
// We use a simple approach: re-read each file by constructing the path
|
||||
let files_out: String = exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null")
|
||||
let files: String = str_trim(files_out)
|
||||
if str_eq(files, "") {
|
||||
println("[sandbox] no sandboxes found")
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parse line-by-line via exec + while loop using index
|
||||
// El has no split(), so we iterate using exec to list files individually
|
||||
let count: Int = 0
|
||||
let i: Int = 0
|
||||
// We use a trick: exec "ls -1" and parse each file
|
||||
// Since El has no string split, we re-use the file listing via a numbered approach
|
||||
let num_files_str: String = str_trim(exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null | wc -l"))
|
||||
let num_files: Int = str_to_int(num_files_str)
|
||||
|
||||
while i < num_files {
|
||||
let file_path_raw: String = str_trim(exec("ls " + FORGE_SANDBOXES_DIR + "/*.json 2>/dev/null | sed -n '" + int_to_str(i + 1) + "p'"))
|
||||
if !str_eq(file_path_raw, "") {
|
||||
let sb_json: String = fs_read(file_path_raw)
|
||||
if !str_eq(sb_json, "") {
|
||||
let sb_id: String = json_get_string(sb_json, "id")
|
||||
let sb_name: String = json_get_string(sb_json, "name")
|
||||
let sb_status: String = json_get_string(sb_json, "status")
|
||||
let sb_ts: Int = json_get_int(sb_json, "created_at")
|
||||
let n_parts: Int = sandbox_participants_count(sb_json)
|
||||
println(" [" + sb_status + "] " + sb_id + " " + sb_name + " (" + int_to_str(n_parts) + " participants) created=" + int_to_str(sb_ts))
|
||||
let count = count + 1
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
println("[sandbox] no sandboxes found")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── forge soul sandbox join ──────────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_join_main() -> String {
|
||||
// args: [0]="soul" [1]="sandbox" [2]="join" [3]=<id> [4]=<slug>
|
||||
let argv: [String] = args()
|
||||
let id: String = ""
|
||||
let slug: String = ""
|
||||
if len(argv) > 3 { let id = get(argv, 3) }
|
||||
if len(argv) > 4 { let slug = get(argv, 4) }
|
||||
if str_eq(id, "") || str_eq(slug, "") {
|
||||
println("[sandbox] usage: forge soul sandbox join <sandbox-id> <slug>")
|
||||
return ""
|
||||
}
|
||||
|
||||
let sb_json: String = sandbox_read(id)
|
||||
if str_eq(sb_json, "") {
|
||||
println("[sandbox] error: sandbox not found: " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
let status: String = json_get_string(sb_json, "status")
|
||||
if str_eq(status, "closed") {
|
||||
println("[sandbox] error: sandbox is closed: " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Check if slug already present
|
||||
let already: Int = 0
|
||||
let i: Int = 0
|
||||
while i < 50 {
|
||||
let p: String = json_get(sb_json, "participants." + int_to_str(i))
|
||||
if str_eq(p, "") {
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
if str_eq(p, slug) {
|
||||
let already = 1
|
||||
let i = i + 50 // break
|
||||
} else {
|
||||
let i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if already == 1 {
|
||||
println("[sandbox] " + slug + " is already a participant in " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Build new participants array by appending
|
||||
let n: Int = sandbox_participants_count(sb_json)
|
||||
let new_parts: String = "["
|
||||
let j: Int = 0
|
||||
while j < n {
|
||||
let p: String = json_get(sb_json, "participants." + int_to_str(j))
|
||||
if j > 0 { let new_parts = new_parts + "," }
|
||||
let new_parts = new_parts + "\"" + str_escape_json(p) + "\""
|
||||
let j = j + 1
|
||||
}
|
||||
if n > 0 { let new_parts = new_parts + "," }
|
||||
let new_parts = new_parts + "\"" + str_escape_json(slug) + "\""
|
||||
let new_parts = new_parts + "]"
|
||||
|
||||
// Reconstruct sandbox JSON with updated participants
|
||||
let sb_id: String = json_get_string(sb_json, "id")
|
||||
let sb_name: String = json_get_string(sb_json, "name")
|
||||
let sb_ts: Int = json_get_int(sb_json, "created_at")
|
||||
let sb_topic: String = json_get_string(sb_json, "topic")
|
||||
let updated: String = "{\"id\":\"" + str_escape_json(sb_id) +
|
||||
"\",\"name\":\"" + str_escape_json(sb_name) +
|
||||
"\",\"created_at\":" + int_to_str(sb_ts) +
|
||||
",\"status\":\"active\"" +
|
||||
",\"participants\":" + new_parts +
|
||||
",\"topic\":\"" + str_escape_json(sb_topic) + "\"" +
|
||||
",\"working_memory\":[]" +
|
||||
",\"record\":[]}"
|
||||
sandbox_write(id, updated)
|
||||
|
||||
println("[sandbox] " + slug + " joined sandbox " + id)
|
||||
log_event("sandbox_join", sb_name, "id=" + id + " slug=" + slug)
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── forge soul sandbox close ─────────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_close_main() -> String {
|
||||
// args: [0]="soul" [1]="sandbox" [2]="close" [3]=<id>
|
||||
let argv: [String] = args()
|
||||
let id: String = ""
|
||||
if len(argv) > 3 { let id = get(argv, 3) }
|
||||
if str_eq(id, "") {
|
||||
println("[sandbox] usage: forge soul sandbox close <sandbox-id>")
|
||||
return ""
|
||||
}
|
||||
|
||||
let sb_json: String = sandbox_read(id)
|
||||
if str_eq(sb_json, "") {
|
||||
println("[sandbox] error: sandbox not found: " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
let status: String = json_get_string(sb_json, "status")
|
||||
if str_eq(status, "closed") {
|
||||
println("[sandbox] sandbox already closed: " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
let sb_id: String = json_get_string(sb_json, "id")
|
||||
let sb_name: String = json_get_string(sb_json, "name")
|
||||
let sb_ts: Int = json_get_int(sb_json, "created_at")
|
||||
let sb_topic: String = json_get_string(sb_json, "topic")
|
||||
let close_ts: Int = unix_timestamp()
|
||||
|
||||
// Rebuild participants array
|
||||
let n: Int = sandbox_participants_count(sb_json)
|
||||
let parts_json: String = "["
|
||||
let j: Int = 0
|
||||
while j < n {
|
||||
let p: String = json_get(sb_json, "participants." + int_to_str(j))
|
||||
if j > 0 { let parts_json = parts_json + "," }
|
||||
let parts_json = parts_json + "\"" + str_escape_json(p) + "\""
|
||||
let j = j + 1
|
||||
}
|
||||
let parts_json = parts_json + "]"
|
||||
|
||||
// Write closed record with closed_at timestamp
|
||||
let closed: String = "{\"id\":\"" + str_escape_json(sb_id) +
|
||||
"\",\"name\":\"" + str_escape_json(sb_name) +
|
||||
"\",\"created_at\":" + int_to_str(sb_ts) +
|
||||
",\"closed_at\":" + int_to_str(close_ts) +
|
||||
",\"status\":\"closed\"" +
|
||||
",\"participants\":" + parts_json +
|
||||
",\"topic\":\"" + str_escape_json(sb_topic) + "\"" +
|
||||
",\"working_memory\":[]" +
|
||||
",\"record\":[{\"ts\":" + int_to_str(close_ts) + ",\"event\":\"sandbox_closed\",\"by\":\"forge\"}]}"
|
||||
sandbox_write(id, closed)
|
||||
|
||||
println("[sandbox] closed: " + id + " (" + sb_name + ")")
|
||||
println("[sandbox] record: " + sandbox_path(id))
|
||||
log_event("sandbox_close", sb_name, "id=" + id + " closed_at=" + int_to_str(close_ts))
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── forge soul sandbox status ────────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_status_main() -> String {
|
||||
// args: [0]="soul" [1]="sandbox" [2]="status" [3]=<id>
|
||||
let argv: [String] = args()
|
||||
let id: String = ""
|
||||
if len(argv) > 3 { let id = get(argv, 3) }
|
||||
if str_eq(id, "") {
|
||||
println("[sandbox] usage: forge soul sandbox status <sandbox-id>")
|
||||
return ""
|
||||
}
|
||||
|
||||
let sb_json: String = sandbox_read(id)
|
||||
if str_eq(sb_json, "") {
|
||||
println("[sandbox] error: sandbox not found: " + id)
|
||||
return ""
|
||||
}
|
||||
|
||||
let sb_id: String = json_get_string(sb_json, "id")
|
||||
let sb_name: String = json_get_string(sb_json, "name")
|
||||
let sb_status: String = json_get_string(sb_json, "status")
|
||||
let sb_ts: Int = json_get_int(sb_json, "created_at")
|
||||
let sb_topic: String = json_get_string(sb_json, "topic")
|
||||
let n: Int = sandbox_participants_count(sb_json)
|
||||
|
||||
println("[sandbox] " + sb_id)
|
||||
println(" name: " + sb_name)
|
||||
println(" status: " + sb_status)
|
||||
println(" topic: " + sb_topic)
|
||||
println(" created: " + int_to_str(sb_ts))
|
||||
println(" participants (" + int_to_str(n) + "):")
|
||||
let k: Int = 0
|
||||
while k < n {
|
||||
let p: String = json_get(sb_json, "participants." + int_to_str(k))
|
||||
println(" - " + p)
|
||||
let k = k + 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── forge soul sandbox dispatch ──────────────────────────────────────────────
|
||||
|
||||
fn soul_sandbox_main() -> String {
|
||||
// args: [0]="soul" [1]="sandbox" [2]=<subcmd> ...
|
||||
let argv: [String] = args()
|
||||
let subcmd: String = ""
|
||||
if len(argv) > 2 {
|
||||
let subcmd = get(argv, 2)
|
||||
}
|
||||
if str_eq(subcmd, "create") {
|
||||
soul_sandbox_create_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "list") {
|
||||
soul_sandbox_list_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "join") {
|
||||
soul_sandbox_join_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "close") {
|
||||
soul_sandbox_close_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "status") {
|
||||
soul_sandbox_status_main()
|
||||
} else {
|
||||
println("[sandbox] usage: forge soul sandbox <create|list|join|close|status> [args]")
|
||||
println("")
|
||||
println(" forge soul sandbox create <name> [slug1 slug2 ...] — create sandbox")
|
||||
println(" forge soul sandbox list — list all sandboxes")
|
||||
println(" forge soul sandbox join <id> <slug> — add soul to sandbox")
|
||||
println(" forge soul sandbox close <id> — close and record sandbox")
|
||||
println(" forge soul sandbox status <id> — show sandbox state")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── soul_main — dispatch ─────────────────────────────────────────────────────
|
||||
//
|
||||
// args() layout when invoked as: forge soul <subcmd> [slug]
|
||||
// args()[0] = "soul"
|
||||
// args()[1] = <subcmd> e.g. "install"
|
||||
// args()[2] = [slug] e.g. "bobby-anderson"
|
||||
|
||||
fn soul_main() -> String {
|
||||
let argv: [String] = args()
|
||||
let subcmd: String = ""
|
||||
if len(argv) > 1 {
|
||||
let subcmd = get(argv, 1)
|
||||
}
|
||||
|
||||
if str_eq(subcmd, "install") {
|
||||
soul_install_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "start") {
|
||||
soul_start_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "stop") {
|
||||
soul_stop_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "status") {
|
||||
soul_status_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "wire") {
|
||||
soul_wire_main()
|
||||
} else {
|
||||
if str_eq(subcmd, "sandbox") {
|
||||
soul_sandbox_main()
|
||||
} else {
|
||||
println("[soul] usage: forge soul <install|start|stop|status|wire|sandbox> [slug]")
|
||||
println("")
|
||||
println(" forge soul install <slug> — write launchd plist, load soul as resident")
|
||||
println(" forge soul install --all — install all souls from registry")
|
||||
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 (create|list|join|close|status)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user