#!/usr/bin/env nu # prvng integration — OCI domain artifact management for federated integration modes. # Ruta β: this surface lives entirely in provisioning per ADR-042. # # Subcommand tree: # integration domain publish [--ref ] [--registry ] # integration domain pull [--registry ] # integration domain describe [--registry ] # integration domain verify [--registry ] # integration domain diff : : # integration ecosystem domains [--registry ] export-env { let lib_dirs_raw = ($env.NU_LIB_DIRS? | default "") let current_lib_dirs = if ($lib_dirs_raw | type) == "string" { if ($lib_dirs_raw | is-empty) { [] } else { ($lib_dirs_raw | split row ":") } } else { $lib_dirs_raw } let dynamic = ($env.PROVISIONING? | default "" | path join "core" "nulib") $env.NU_LIB_DIRS = ([ "/opt/provisioning/core/nulib" "/usr/local/provisioning/core/nulib" ] | append $current_lib_dirs | append (if ($dynamic | is-not-empty) { [$dynamic] } else { [] })) } use platform/oci/domain_client.nu [ domain-push domain-pull domain-describe domain-verify ecosystem-list-domains ecosystem-list-modes resolve-registry mode-push mode-pull ] use platform/integrations/context_assembler.nu [ assemble-context build-secret-delivery-context ] def int-err [msg: string] { error make --unspanned { msg: $msg } } # Return the value after --flag in args, or "" when absent. def parse-flag [flag: string, args: list]: nothing -> string { let hits = ($args | enumerate | where { |e| $e.item == $flag }) if ($hits | is-empty) { return "" } let next = ($hits | first | get index) + 1 if $next < ($args | length) { $args | get $next } else { "" } } def print-check [label: string, pass: bool]: nothing -> nothing { let icon = if $pass { "✓" } else { "✗" } let color = if $pass { "green_bold" } else { "red_bold" } print $" ($icon | ansi $color) ($label)" } # ── domain subcommands ──────────────────────────────────────────────────────── def resolve-workspace-participant []: nothing -> string { let manifest = ($env.PWD | path join ".ontology" "manifest.ncl") if not ($manifest | path exists) { return "" } let ip = ($env.NICKEL_IMPORT_PATH? | default ($env.PROVISIONING? | default "")) let ncl = (do { ^nickel export --import-path $ip $manifest } | complete) if $ncl.exit_code != 0 { return "" } $ncl.stdout | from json | get -o registry_provides.participant | default "" } def cmd-domain-publish [args: list]: nothing -> nothing { let path = $args | get 0? | default "" if ($path | is-empty) { int-err "Usage: integration domain publish [--ref ] [--registry ]" } let flag_ref = (parse-flag "--ref" $args) let flag_registry = (parse-flag "--registry" $args) let flag_participant = (parse-flag "--participant" $args) # participant: explicit flag → second positional arg → workspace manifest → error let participant = if ($flag_participant | is-not-empty) { $flag_participant } else { let pos = $args | get 1? | default "" if ($pos | is-not-empty) and not ($pos | str starts-with "-") { $pos } else { let ws = (resolve-workspace-participant) if ($ws | is-not-empty) { $ws } else { int-err "participant required: pass as second arg or --participant (or declare registry_provides in .ontology/manifest.ncl)" "" } } } let result = if ($flag_ref | is-not-empty) { if ($flag_registry | is-not-empty) { domain-push $path $participant --ref $flag_ref --registry $flag_registry } else { domain-push $path $participant --ref $flag_ref } } else { if ($flag_registry | is-not-empty) { domain-push $path $participant --registry $flag_registry } else { domain-push $path $participant } } print $"Pushed ($result.ref)" print $"Digest: ($result.digest)" } # Split "/" into {participant, id}. Errors when no "/" present. def split-domain-ref [ref: string]: nothing -> record { let parts = ($ref | split row "/" | filter { |p| $p | is-not-empty }) if ($parts | length) < 2 { error make --unspanned { msg: $"Expected /, got: ($ref)" } } { participant: ($parts | first), id: ($parts | skip 1 | str join "/") } } def cmd-domain-pull [args: list]: nothing -> nothing { let ref_arg = $args | get 0? | default "" let version = $args | get 1? | default "" if ($ref_arg | is-empty) or ($version | is-empty) { int-err "Usage: integration domain pull / [--registry ]" } let parts = (split-domain-ref $ref_arg) let flag_registry = (parse-flag "--registry" $args) let cache = if ($flag_registry | is-not-empty) { domain-pull $parts.participant $parts.id $version --registry $flag_registry } else { domain-pull $parts.participant $parts.id $version } print $"Pulled ($ref_arg):($version) → ($cache)" } def cmd-domain-describe [args: list]: nothing -> nothing { let ref_arg = $args | get 0? | default "" let version = $args | get 1? | default "" if ($ref_arg | is-empty) or ($version | is-empty) { int-err "Usage: integration domain describe / " } let parts = (split-domain-ref $ref_arg) let info = (domain-describe $parts.participant $parts.id $version) print $"Ref: ($info.ref)" print $"ArtifactType: ($info.artifact_type)" print $"Digest: ($info.digest)" print "Layers:" for layer in $info.layers { let mt = $layer | get -o mediaType | default "" let dg = $layer | get -o digest | default "" let dg_short = if ($dg | str length) > 20 { $dg | str substring 0..20 } else { $dg } print $" ($mt) ($dg_short)..." } } def cmd-domain-verify [args: list]: nothing -> nothing { let ref_arg = $args | get 0? | default "" let version = $args | get 1? | default "" let cosign_key = (parse-flag "--cosign-key" $args) if ($ref_arg | is-empty) or ($version | is-empty) { int-err "Usage: integration domain verify / [--cosign-key ]" } let parts = (split-domain-ref $ref_arg) let resolved_key = if ($cosign_key | is-not-empty) { $cosign_key } else { resolve-cosign-pubkey } let result = if ($resolved_key | is-not-empty) { domain-verify $parts.participant $parts.id $version --cosign-key $resolved_key } else { domain-verify $parts.participant $parts.id $version } print $"Verifying ($result.ref)..." print-check "artifact-type" $result.checks.artifact_type print-check "contract.ncl present" $result.checks.contract_present print-check "contract.ncl typechecks" $result.checks.contract_typechecks print-check "digest consistent" $result.checks.digest_ok print-check "cosign signature" $result.checks.cosign_signature if ($result.cosign_key | is-empty) { print " (cosign key not configured — signature check skipped)" } if not $result.pass { print "" for e in $result.errors { print $" Error: ($e)" } exit 1 } print "" print $"(ansi green_bold)OK(ansi reset)" } # Parse /: into its parts. def split-domain-versioned-ref [ref: string]: nothing -> record { let head_tail = ($ref | split row ":") let head = ($head_tail | get 0? | default "") let version = ($head_tail | get 1? | default "") let pid = ($head | split row "/") { participant: ($pid | get 0? | default ""), id: ($pid | skip 1 | str join "/"), version: $version, } } def cmd-domain-diff [args: list]: nothing -> nothing { let a = $args | get 0? | default "" let b = $args | get 1? | default "" if ($a | is-empty) or ($b | is-empty) { int-err "Usage: integration domain diff /: /:" } let pa = (split-domain-versioned-ref $a) let pb = (split-domain-versioned-ref $b) if ($pa.participant | is-empty) or ($pa.id | is-empty) or ($pa.version | is-empty) { int-err $"Malformed ref: ($a) — expected /:" } if ($pb.participant | is-empty) or ($pb.id | is-empty) or ($pb.version | is-empty) { int-err $"Malformed ref: ($b) — expected /:" } let flag_registry = (parse-flag "--registry" $args) let cache_a = if ($flag_registry | is-not-empty) { domain-pull $pa.participant $pa.id $pa.version --registry $flag_registry } else { domain-pull $pa.participant $pa.id $pa.version } let cache_b = if ($flag_registry | is-not-empty) { domain-pull $pb.participant $pb.id $pb.version --registry $flag_registry } else { domain-pull $pb.participant $pb.id $pb.version } let info_a = if ($flag_registry | is-not-empty) { domain-describe $pa.participant $pa.id $pa.version --registry $flag_registry } else { domain-describe $pa.participant $pa.id $pa.version } let info_b = if ($flag_registry | is-not-empty) { domain-describe $pb.participant $pb.id $pb.version --registry $flag_registry } else { domain-describe $pb.participant $pb.id $pb.version } print "" print $"(ansi white_bold)Domain diff(ansi reset) ($a) ↔ ($b)" print "" # 1. Identity let same_identity = ($pa.participant == $pb.participant) and ($pa.id == $pb.id) if not $same_identity { let id_a = $"($pa.participant)/($pa.id)" let id_b = $"($pb.participant)/($pb.id)" print $" (ansi yellow)note(ansi reset) comparing across different domain identities — A=($id_a) B=($id_b)" print "" } # 2. Digest let same_digest = ($info_a.raw == $info_b.raw) if $same_digest { print $" (ansi green)=(ansi reset) manifest identical (no structural change)" return } print $" (ansi cyan)≠(ansi reset) manifests differ" print $" ($a): digest ($info_a.digest)" print $" ($b): digest ($info_b.digest)" print "" # 3. Artifact type if $info_a.artifact_type != $info_b.artifact_type { print $" (ansi red)Δ artifactType(ansi reset) ($info_a.artifact_type) → ($info_b.artifact_type)" } # 4. Layer set comparison (mediaType + size + digest by layer index) let layers_a = ($info_a.layers | each { |l| { mt: ($l.mediaType? | default ""), size: ($l.size? | default 0), digest: ($l.digest? | default "") } }) let layers_b = ($info_b.layers | each { |l| { mt: ($l.mediaType? | default ""), size: ($l.size? | default 0), digest: ($l.digest? | default "") } }) print "" print " layers:" print $" A: ($layers_a | length) B: ($layers_b | length)" let max_layers = (if (($layers_a | length) > ($layers_b | length)) { $layers_a | length } else { $layers_b | length }) for i in 0..($max_layers - 1) { let la = ($layers_a | get -i $i | default null) let lb = ($layers_b | get -i $i | default null) if $la == null { print $" (ansi green)+ #($i)(ansi reset) ($lb.mt) ($lb.digest)" } else if $lb == null { print $" (ansi red)- #($i)(ansi reset) ($la.mt) ($la.digest)" } else if $la.digest == $lb.digest { print $" (ansi dark_gray)= #($i)(ansi reset) ($la.mt) unchanged" } else { print $" (ansi cyan)Δ #($i)(ansi reset) ($la.mt)" print $" A: ($la.digest)" print $" B: ($lb.digest)" } } # 5. Contract content diff (text — line-level, system 'diff' if available) let contract_a = ($cache_a | path join "contract.ncl") let contract_b = ($cache_b | path join "contract.ncl") if ($contract_a | path exists) and ($contract_b | path exists) { print "" print " contract.ncl:" let diff_bin = (which diff) if ($diff_bin | is-empty) { let same = ((open --raw $contract_a) == (open --raw $contract_b)) if $same { print " (ansi green)= identical(ansi reset)" } else { print " (ansi cyan)≠ differs(ansi reset) (install diff for line-level output)" } } else { let r = (do { ^diff -u $contract_a $contract_b } | complete) if $r.exit_code == 0 { print " (ansi green)= identical(ansi reset)" } else { # Indent diff output for visual nesting let lines = ($r.stdout | lines) if ($lines | is-empty) { print " (ansi cyan)≠ differs(ansi reset) (no readable diff)" } else { for ln in $lines { print $" ($ln)" } } } } } print "" } def cmd-domain [args: list]: nothing -> nothing { let sub = $args | get 0? | default "" let rest = if ($args | length) > 1 { $args | skip 1 } else { [] } match $sub { "publish" => { cmd-domain-publish $rest } "pull" => { cmd-domain-pull $rest } "describe" | "desc" => { cmd-domain-describe $rest } "verify" => { cmd-domain-verify $rest } "diff" => { cmd-domain-diff $rest } _ => { print "integration domain " print "" print "Subcommands:" print " publish Push domain artifact to registry" print " pull / Pull domain artifact to local cache" print " describe / Show OCI manifest details" print " verify / Verify artifact integrity and contract" print " diff /: /: Structural diff between two versions" } } } # ── mode subcommand ─────────────────────────────────────────────────────────── def cmd-mode-publish [args: list]: nothing -> nothing { let path = $args | get 0? | default "" let participant = $args | get 1? | default "" let mode_id = $args | get 2? | default "" let version = $args | get 3? | default "" let flag_registry = (parse-flag "--registry" $args) if ($path | is-empty) or ($participant | is-empty) or ($mode_id | is-empty) or ($version | is-empty) { int-err "Usage: integration mode publish [--registry ]" } let result = if ($flag_registry | is-not-empty) { mode-push $path $participant $mode_id $version --registry $flag_registry } else { mode-push $path $participant $mode_id $version } print $"Published mode artifact:" print $" ref: ($result.ref)" print $" digest: ($result.digest)" let cosign_key = (resolve-cosign-pubkey) if ($cosign_key | is-not-empty) { let priv_key = ($env | get -o "COSIGN_KEY_PATH" | default "") if ($priv_key | is-not-empty) { print $"Signing ($result.ref) with ($cosign_key)..." let image_ref = ($result.ref | str replace --regex ':.*$' '' | $"($in)@($result.digest)") let sign = (do { COSIGN_PASSWORD="" ^cosign sign --key $priv_key --yes $image_ref } | complete) if $sign.exit_code == 0 { print $" (ansi green_bold)✓(ansi reset) signed" } else { let sign_err = ($sign.stderr | str trim | lines | last | default "sign failed") print $" (ansi yellow_bold)⚠(ansi reset) signing failed: ($sign_err)" } } else { print $" (ansi yellow_bold)⚠(ansi reset) COSIGN_KEY_PATH not set — artifact not signed" } } } # Parse / into a record. Caller validates mode_id present. def split-mode-ref [ref: string]: nothing -> record { let parts = ($ref | split row "/") let participant = ($parts | first | default "") let mode_id = ($parts | skip 1 | str join "/") { participant: $participant, mode_id: $mode_id } } def cmd-mode-pull [args: list]: nothing -> nothing { let pm = $args | get 0? | default "" let version = $args | get 1? | default "" if ($pm | is-empty) or ($version | is-empty) { int-err "Usage: integration mode pull / [--registry ]" } let parts = (split-mode-ref $pm) if ($parts.mode_id | is-empty) { int-err "Mode ref must be /" } let flag_registry = (parse-flag "--registry" $args) let cache = if ($flag_registry | is-not-empty) { mode-pull $parts.participant $parts.mode_id $version --registry $flag_registry } else { mode-pull $parts.participant $parts.mode_id $version } print $"Mode artifact pulled to: ($cache)" } def cmd-mode-describe [args: list]: nothing -> nothing { let pm = $args | get 0? | default "" let version = $args | get 1? | default "" if ($pm | is-empty) or ($version | is-empty) { int-err "Usage: integration mode describe / [--registry ]" } let parts = (split-mode-ref $pm) if ($parts.mode_id | is-empty) { int-err "Mode ref must be /" } let flag_registry = (parse-flag "--registry" $args) let cache = if ($flag_registry | is-not-empty) { mode-pull $parts.participant $parts.mode_id $version --registry $flag_registry } else { mode-pull $parts.participant $parts.mode_id $version } let decl_path = ($cache | path join "provisioning.ncl") if not ($decl_path | path exists) { int-err $"provisioning.ncl absent in pulled artifact at ($cache)" } let import_path = ($env.NICKEL_IMPORT_PATH? | default ($env.PROVISIONING? | default "")) let ncl = (do { ^nickel export --import-path $import_path $decl_path } | complete) if $ncl.exit_code != 0 { int-err $"Failed to evaluate provisioning.ncl: ($ncl.stderr)" } let mode = ($ncl.stdout | from json) print "" print $"(ansi white_bold)Mode(ansi reset) ($parts.participant)/($parts.mode_id):($version)" print $"(ansi dark_gray)id(ansi reset) ($mode | get -o id | default '(unknown)')" print $"(ansi dark_gray)participant(ansi reset) ($mode | get -o participant | default $parts.participant)" print $"(ansi dark_gray)direction(ansi reset) ($mode | get -o direction | default '(unspecified)')" print $"(ansi dark_gray)trigger(ansi reset) ($mode | get -o trigger | default '(unspecified)')" let domains_used = ($mode | get -o domains_used | default []) print $"(ansi dark_gray)domains_used(ansi reset) ($domains_used | length) entries" let steps = ($mode | get -o steps | default []) print $"(ansi dark_gray)steps(ansi reset) ($steps | length) defined" print "" } def cmd-mode-verify [args: list]: nothing -> nothing { let pm = $args | get 0? | default "" let version = $args | get 1? | default "" if ($pm | is-empty) or ($version | is-empty) { int-err "Usage: integration mode verify / [--registry ]" } let parts = (split-mode-ref $pm) if ($parts.mode_id | is-empty) { int-err "Mode ref must be /" } let flag_registry = (parse-flag "--registry" $args) let cache = if ($flag_registry | is-not-empty) { mode-pull $parts.participant $parts.mode_id $version --registry $flag_registry } else { mode-pull $parts.participant $parts.mode_id $version } mut errors = [] let decl_path = ($cache | path join "provisioning.ncl") let lock_path = ($cache | path join "domains.lock.ncl") let decl_present = ($decl_path | path exists) let lock_present = ($lock_path | path exists) if not $decl_present { $errors = ($errors | append "provisioning.ncl missing") } if not $lock_present { $errors = ($errors | append "domains.lock.ncl missing") } let import_path = ($env.NICKEL_IMPORT_PATH? | default ($env.PROVISIONING? | default "")) mut decl_typecheck_ok = false if $decl_present { let r = (do { ^nickel typecheck --import-path $import_path $decl_path } | complete) if $r.exit_code == 0 { $decl_typecheck_ok = true } else { $errors = ($errors | append $"provisioning.ncl typecheck failed: ($r.stderr | str trim)") } } let manifest_path = ($cache | path join "oci-manifest.json") let artifact_type_ok = if ($manifest_path | path exists) { let m = (open --raw $manifest_path | from json) let at = ($m | get -o artifactType | default "") $at == "application/vnd.ontoref.mode.v1" } else { false } if not $artifact_type_ok { $errors = ($errors | append "artifactType != application/vnd.ontoref.mode.v1") } let pass = $decl_present and $lock_present and $decl_typecheck_ok and $artifact_type_ok print "" print-check "provisioning.ncl present" $decl_present print-check "domains.lock.ncl present" $lock_present print-check "provisioning.ncl typechecks" $decl_typecheck_ok print-check "artifactType vnd.ontoref.mode.v1" $artifact_type_ok print "" if $pass { print $"(ansi green_bold)OK(ansi reset)" } else { for e in $errors { print $" (ansi red)·(ansi reset) ($e)" } exit 1 } } def cmd-mode [args: list]: nothing -> nothing { let sub = $args | get 0? | default "" let rest = if ($args | length) > 1 { $args | skip 1 } else { [] } match $sub { "publish" => { cmd-mode-publish $rest } "pull" => { cmd-mode-pull $rest } "describe" | "desc" => { cmd-mode-describe $rest } "verify" => { cmd-mode-verify $rest } _ => { print "integration mode " print "" print "Subcommands:" print " publish Push mode artifact" print " pull / Pull mode artifact to local cache" print " describe / Show mode metadata + steps" print " verify / Verify integrity + typecheck" } } } # ── invoke subcommand ──────────────────────────────────────────────────────── def cmd-invoke [args: list]: nothing -> nothing { let mode_id = $args | get 0? | default "" if ($mode_id | is-empty) { int-err "Usage: integration invoke --binary [--workspace-root ] [--ncl-import ] [--dry-run]" } let flag_binary = (parse-flag "--binary" $args) let flag_ws_root = (parse-flag "--workspace-root" $args) let flag_ncl_import = (parse-flag "--ncl-import" $args) let dry_run = ($args | any { |a| $a == "--dry-run" }) let binary = if ($flag_binary | is-not-empty) { $flag_binary } else { # Derive participant name from mode-id convention: strip trailing "-" $mode_id | split row "-" | drop | str join "-" } let ws_root = if ($flag_ws_root | is-not-empty) { $flag_ws_root } else { $env.PWD } # Cabling file convention: infra//integrations/.ncl # Look relative to workspace root; fall back to scanning infra/ let direct = ($ws_root | path join "integrations" $"($mode_id).ncl") let cabling_path = if ($direct | path exists) { $direct } else { let infra = ($ws_root | path join "infra") if ($infra | path exists) { let candidates = ( ls $infra | where type == "dir" | get name | each { |d| $d | path join "integrations" $"($mode_id).ncl" } | where { |p| $p | path exists } ) if ($candidates | is-empty) { error make --unspanned { msg: $"No cabling file found for mode ($mode_id) under ($ws_root)" } } $candidates | first } else { error make --unspanned { msg: $"No cabling file found for mode ($mode_id) under ($ws_root)" } } } print $"Assembling context for ($mode_id) from ($cabling_path)..." let ctx = if ($flag_ncl_import | is-not-empty) { assemble-context $cabling_path --workspace-root $ws_root --ncl-import $flag_ncl_import } else { assemble-context $cabling_path --workspace-root $ws_root } let envelope = (build-secret-delivery-context $ctx) if $dry_run { print "-- dry-run: context envelope (secrets redacted) --" let parsed = ($envelope | from json) let redacted = ($parsed | update secrets { |r| $r.secrets | transpose key entry | reduce --fold {} { |row, acc| $acc | upsert $row.key { value: "", source: $row.entry.source } } }) print ($redacted | to json --indent 2) return } print $"Invoking ($binary)..." let result = (do { $envelope | ^$binary } | complete) if $result.exit_code != 0 { print $"($binary) exited ($result.exit_code)" if ($result.stderr | is-not-empty) { print $result.stderr } exit $result.exit_code } print $result.stdout } # ── ecosystem subcommands ───────────────────────────────────────────────────── def cmd-ecosystem [args: list]: nothing -> nothing { let sub = $args | get 0? | default "" let rest = if ($args | length) > 1 { $args | skip 1 } else { [] } match $sub { "domains" => { let flag_registry = (parse-flag "--registry" $rest) let domains = if ($flag_registry | is-not-empty) { ecosystem-list-domains --registry $flag_registry } else { ecosystem-list-domains } if ($domains | is-empty) { print "No domain artifacts found (catalog API may be unavailable)" } else { for d in $domains { let versions = ($d.versions | str join ", ") print $"($d.participant)/($d.id) ($versions)" } } } "modes" => { let flag_registry = (parse-flag "--registry" $rest) let modes = if ($flag_registry | is-not-empty) { ecosystem-list-modes --registry $flag_registry } else { ecosystem-list-modes } if ($modes | is-empty) { print "No mode artifacts found (catalog API may be unavailable)" } else { for m in $modes { let versions = ($m.versions | str join ", ") print $"($m.participant)/($m.id) ($versions)" } } } _ => { print "integration ecosystem " print "" print "Subcommands:" print " domains [--registry ] List all published domain artifacts" print " modes [--registry ] List all published mode artifacts" } } } # ── list subcommand ────────────────────────────────────────────────────────── def cmd-list [args: list]: nothing -> nothing { let live = ($args | any { |a| $a == "--live" }) let flag_registry = (parse-flag "--registry" $args) let flag_ws_dir = (parse-flag "--workspace-dir" $args) if $live { let reg = if ($flag_registry | is-not-empty) { $flag_registry } else { resolve-registry } print $"Registry: ($reg)" print "" let domains = if ($flag_registry | is-not-empty) { ecosystem-list-domains --registry $flag_registry } else { ecosystem-list-domains } if ($domains | is-not-empty) { print "Domains:" for d in $domains { let versions = ($d.versions | str join ", ") print $" ($d.participant)/($d.id) ($versions)" } } else { print "Domains: (none)" } print "" let modes = if ($flag_registry | is-not-empty) { ecosystem-list-modes --registry $flag_registry } else { ecosystem-list-modes } if ($modes | is-not-empty) { print "Modes:" for m in $modes { let versions = ($m.versions | str join ", ") print $" ($m.participant)/($m.id) ($versions)" } } else { print "Modes: (none)" } return } let root = if ($flag_ws_dir | is-not-empty) { $flag_ws_dir } else { $env.PWD } # Collect cabling files from infra/*/integrations/*.ncl let infra = ($root | path join "infra") let cablings = if ($infra | path exists) { ls $infra | where type == "dir" | get name | each { |ws_dir| let int_dir = $ws_dir | path join "integrations" if ($int_dir | path exists) { ls $int_dir | where name =~ '\.ncl$' | get name } else { [] } } | flatten } else { [] } # Collect cached domains. Cache layout: ~/.cache/ontoref/domains//// # T5: extract participant from the directory hierarchy; fallback for legacy # caches without the participant level (versions live directly under id/). let domain_cache = ($env.HOME | path join ".cache" "ontoref" "domains") let cached_domains = if ($domain_cache | path exists) { try { ls $domain_cache | where type == "dir" | get name | each { |participant_dir| let participant = ($participant_dir | path basename) # Heuristic: if the participant_dir contains version-shaped subdirs directly, # this is the legacy flat layout (id only) — treat it as { id: , no participant }. let inner = (ls $participant_dir | where type == "dir" | get name) let inner_has_subdir_with_versions = ($inner | any { |d| (ls $d | where type == "dir" | length) > 0 }) if $inner_has_subdir_with_versions { # New layout: /// $inner | each { |id_dir| let versions = (ls $id_dir | where type == "dir" | get name | each { |v| $v | path basename } | str join ", ") { participant: $participant, id: ($id_dir | path basename), versions: $versions } } } else { # Legacy flat layout: // let versions = ($inner | each { |v| $v | path basename } | str join ", ") [{ participant: "(legacy)", id: $participant, versions: $versions }] } } | flatten } catch { [] } } else { [] } if ($cablings | is-empty) and ($cached_domains | is-empty) { print "No integration modes or cached domains found." print "Use: prvng integration subscribe --mode-file --workspace-dir " return } if ($cablings | is-not-empty) { print "Integration modes (cabling files):" for c in $cablings { let mode_id = ($c | path basename | str replace ".ncl" "") let ws = ($c | path dirname | path dirname | path basename) print $" ($mode_id) [workspace: ($ws)] ($c)" } } if ($cached_domains | is-not-empty) { print "" print "Cached domains:" for d in $cached_domains { print $" ($d.participant)/($d.id) ($d.versions)" } } } # ── describe subcommand ─────────────────────────────────────────────────────── def cmd-describe [args: list]: nothing -> nothing { let mode_id = $args | get 0? | default "" let flag_mode_file = (parse-flag "--mode-file" $args) let flag_ncl_import = (parse-flag "--ncl-import" $args) if ($mode_id | is-empty) or ($flag_mode_file | is-empty) { int-err "Usage: integration describe --mode-file [--ncl-import ]" } let import_path = if ($flag_ncl_import | is-not-empty) { $flag_ncl_import } else { $env.PROVISIONING? | default "/opt/provisioning" } let r = (do { ^nickel export --import-path $import_path $flag_mode_file } | complete) if $r.exit_code != 0 { int-err $"Failed to evaluate mode file: ($r.stderr)" } let mode = ($r.stdout | from json | get mode) print $"Mode: ($mode.id)" print $"Participant: ($mode.participant)" print $"Direction: ($mode.direction)" print $"Trigger: ($mode.trigger)" print "" print "Domains used:" for d in ($mode | get -o domains_used | default []) { print $" ($d.id) ($d.version)" } print "" print "Steps:" for s in ($mode | get -o steps | default []) { let deps = ($s | get -o depends_on | default [] | each { |d| $d.step } | str join ", ") let dep_str = if ($deps | is-not-empty) { $" ← ($deps)" } else { "" } print $" ($s.id)($dep_str) — ($s.action)" } } # ── subscribe subcommand ────────────────────────────────────────────────────── def resolve-cosign-pubkey []: nothing -> string { let from_env = ($env | get -o "ONTOREF_COSIGN_PUBKEY" | default "") if ($from_env | is-not-empty) { return $from_env } let prov = ($env.PROVISIONING? | default "") let catalog_root = ($env.PROVISIONING_CATALOG_PATH? | default (if ($prov | is-not-empty) { $prov | path join "catalog" } else { "" })) if ($catalog_root | is-not-empty) { let catalog_key = ($catalog_root | path join "domains/_signing/cosign.pub") if ($catalog_key | path exists) { return $catalog_key } } "" } def resolve-semver-range [range: string]: nothing -> string { let m = ($range | parse --regex '(\d+\.\d+\.\d+)') if ($m | is-empty) { $range | str trim } else { $m | first | get capture0 } } def domain-bindings-for-scaffold [participant: string, domain_id: string, version: string]: nothing -> list { let cache = ($env.HOME | path join ".cache" "ontoref" "domains" $participant $domain_id $version) let ex_path = ($cache | path join "example.json") if ($ex_path | path exists) { let ex = (open $ex_path) let secrets = ($ex | get -o secrets | default {}) if ($secrets | describe | str starts-with "record") and (($secrets | columns | length) > 0) { return ($secrets | columns | each { |fld| let source = ($secrets | get $fld | get -o source | default "sops") if $source == "sops" { $" # REQUIRED — SOPS-encrypted\n \"($domain_id).($fld)\" = { kind = \"sops\", path = \"secrets/.sops.yaml\", key = \"\" }," } else if $source == "component_output" { $" # DEFAULT — resolved from component output\n \"($domain_id).($fld)\" = { kind = \"component\", name = \"\", field = \"\" }," } else { $" # DEFAULT — literal value\n \"($domain_id).($fld)\" = { kind = \"literal\", value = \"\" }," } }) } } # Domain-aware fallbacks for core domains without example.json match $domain_id { "event-emission" => { [ $" # REQUIRED — stable subject prefix for this workspace + mode\n \"event-emission.subject_prefix\" = { kind = \"literal\", value = \"ws..\" },", ] } "result-reporting" => { [ " # result-reporting is output-only — no bindings required" ] } _ => { [ $" # REQUIRED — fill bindings for ($domain_id)\n # \"($domain_id).\" = { kind = \"literal\", value = \"\" }," ] } } } def build-cabling-scaffold [mode: record, workspace: string, registry: string, ncl_import: string]: nothing -> string { let mode_id = $mode.id let domains = ($mode | get -o domains_used | default []) mut binding_blocks = [] for d in $domains { let version = (resolve-semver-range $d.version) let participant = ($d | get -o participant | default ($mode | get -o participant | default "unknown")) let lines = (domain-bindings-for-scaffold $participant $d.id $version) $binding_blocks = ($binding_blocks | append $" # ──── ($d.id) ──────────────────────────") $binding_blocks = ($binding_blocks | append $lines) $binding_blocks = ($binding_blocks | append "") } let bindings_body = ($binding_blocks | str join "\n") $"# Cabling for mode ($mode_id) in workspace ($workspace). # Generated skeleton — operator must fill in REQUIRED bindings before invoking. # Validate: nickel export --import-path infra/($workspace)/integrations/($mode_id).ncl let cabling = import \"schemas/lib/integration/cabling.ncl\" in { config = { mode_id = \"($mode_id)\", workspace = \"($workspace)\", bindings = { ($bindings_body) }, } | cabling.Cabling, } " } def cmd-subscribe [args: list]: nothing -> nothing { let mode_id = $args | get 0? | default "" if ($mode_id | is-empty) { int-err "Usage: integration subscribe --mode-file --workspace-dir [--registry ] [--ncl-import ]" } let flag_mode_file = (parse-flag "--mode-file" $args) let flag_ws_dir = (parse-flag "--workspace-dir" $args) let flag_registry = (parse-flag "--registry" $args) let flag_ncl_import = (parse-flag "--ncl-import" $args) if ($flag_mode_file | is-empty) or ($flag_ws_dir | is-empty) { int-err "subscribe requires --mode-file and --workspace-dir" } let registry = if ($flag_registry | is-not-empty) { $flag_registry } else { resolve-registry } let import_path = if ($flag_ncl_import | is-not-empty) { $flag_ncl_import } else { $env.PROVISIONING? | default "/opt/provisioning" } let r = (do { ^nickel export --import-path $import_path $flag_mode_file } | complete) if $r.exit_code != 0 { int-err $"Failed to evaluate mode file ($flag_mode_file): ($r.stderr)" } let mode = ($r.stdout | from json | get mode) print $"Mode: ($mode.id) participant: ($mode.participant)" print $"Registry: ($registry)" print "" # Pull domains and verify signatures when a cosign key is resolvable. # A resolved key turns unsigned artifacts into hard errors; absent key skips silently. let cosign_key = (resolve-cosign-pubkey) for d in ($mode | get -o domains_used | default []) { let version = (resolve-semver-range $d.version) # participant field in domains_used takes precedence; fall back to mode.participant let participant = ($d | get -o participant | default ($mode | get -o participant | default "")) if ($participant | is-empty) { int-err $"Domain ($d.id) has no participant — add participant field to domains_used entry or declare mode.participant" } print $"Pulling ($participant)/($d.id):($version)..." let cache = (domain-pull $participant $d.id $version --registry $registry) print $" cached → ($cache)" let verify_result = if ($cosign_key | is-not-empty) { domain-verify $participant $d.id $version --registry $registry --cosign-key $cosign_key } else { null } if $verify_result != null { if $verify_result.pass { print $" (ansi green_bold)✓(ansi reset) signature verified" } else { let errs = ($verify_result.errors | str join "; ") int-err $"Domain ($d.id):($version) failed verification: ($errs)" } } } # Resolve workspace name from infra/ directory structure let infra = ($flag_ws_dir | path join "infra") let ws_name = if ($infra | path exists) { let dirs = (ls $infra | where type == "dir" | get name) if ($dirs | is-empty) { $flag_ws_dir | path basename } else { $dirs | first | path basename } } else { $flag_ws_dir | path basename } let integrations_dir = if ($infra | path exists) { $infra | path join $ws_name | path join "integrations" } else { $flag_ws_dir | path join "integrations" } mkdir $integrations_dir let cabling_path = ($integrations_dir | path join $"($mode_id).ncl") if ($cabling_path | path exists) { print "" print $"Cabling already exists: ($cabling_path)" print "Skipping scaffold. Delete the file to regenerate." } else { let scaffold = (build-cabling-scaffold $mode $ws_name $registry $import_path) $scaffold | save $cabling_path print "" print $"Scaffolded cabling → ($cabling_path)" print " Edit REQUIRED bindings before running invoke." } } # ── validate subcommand ─────────────────────────────────────────────────────── def check-resolver [resolver: record, ws_root: string]: nothing -> record { match $resolver.kind { "sops" => { let path = if ($resolver.path | str starts-with "/") { $resolver.path } else { $ws_root | path join $resolver.path } if ($path | path exists) { { ok: true, msg: $"($path) ✓" } } else { { ok: false, msg: $"SOPS file not found: ($path)" } } } "component" => { let infra = ($ws_root | path join "infra") let comp_ncl = if ($infra | path exists) { let dirs = (ls $infra | where type == "dir" | get name) if ($dirs | is-empty) { ($ws_root | path join "components" $"($resolver.name).ncl") } else { $dirs | first | path join "components" $"($resolver.name).ncl" } } else { $ws_root | path join "components" $"($resolver.name).ncl" } if ($comp_ncl | path exists) { { ok: true, msg: $"($comp_ncl) ✓" } } else { { ok: false, msg: $"Component NCL not found: ($comp_ncl)" } } } "literal" => { let v = ($resolver.value | into string) if ($v | is-not-empty) and ($v != "") and (not ($v | str starts-with "<")) { { ok: true, msg: $"literal: ($v)" } } else { { ok: false, msg: $"Literal value looks like an unfilled placeholder: ($v)" } } } "env" => { let val = ($env | get -o $resolver.env_var | default "") if ($val | is-not-empty) { { ok: true, msg: $"$($resolver.env_var) is set" } } else { { ok: false, msg: $"env var ($resolver.env_var) is not set in current environment" } } } _ => { ok: false, msg: $"Unknown resolver kind: ($resolver.kind)" } } } def cmd-validate [args: list]: nothing -> nothing { let mode_id = $args | get 0? | default "" let flag_ws_dir = (parse-flag "--workspace-dir" $args) let flag_ncl_import = (parse-flag "--ncl-import" $args) if ($mode_id | is-empty) or ($flag_ws_dir | is-empty) { int-err "Usage: integration validate --workspace-dir [--ncl-import ]" } let import_path = if ($flag_ncl_import | is-not-empty) { $flag_ncl_import } else { $env.PROVISIONING? | default "/opt/provisioning" } # Find cabling let infra = ($flag_ws_dir | path join "infra") let candidates = if ($infra | path exists) { ls $infra | where type == "dir" | get name | each { |d| $d | path join "integrations" $"($mode_id).ncl" } | where { |p| $p | path exists } } else { let p = ($flag_ws_dir | path join "integrations" $"($mode_id).ncl") if ($p | path exists) { [$p] } else { [] } } if ($candidates | is-empty) { int-err $"No cabling file found for ($mode_id) under ($flag_ws_dir)" } let cabling_path = ($candidates | first) print $"Validating cabling: ($cabling_path)" print "" let r = (do { ^nickel export --import-path $import_path $cabling_path } | complete) if $r.exit_code != 0 { print $" ✗ Schema validation failed:" print $r.stderr exit 1 } let cabling = ($r.stdout | from json | get config) print $" ✓ Nickel schema valid (mode: ($cabling.mode_id), workspace: ($cabling.workspace))" print "" print "Bindings:" mut all_ok = true for entry in ($cabling.bindings | transpose key resolver) { let result = (check-resolver $entry.resolver $flag_ws_dir) let icon = if $result.ok { $"(ansi green_bold)✓(ansi reset)" } else { $"(ansi red_bold)✗(ansi reset)" } print $" ($icon) ($entry.key) [($entry.resolver.kind)] ($result.msg)" if not $result.ok { $all_ok = false } } print "" if $all_ok { print $"(ansi green_bold)OK — all bindings resolve(ansi reset)" } else { print $"(ansi red_bold)FAIL — one or more bindings cannot be resolved(ansi reset)" exit 1 } } # ── help ───────────────────────────────────────────────────────────────────── def _print-integration-help []: nothing -> nothing { print "prvng integration (i) — federated OCI integration modes (ADR-042)" print "" print " Domain artifacts — OCI layers, cosign-signed, stored as domains//:" print " domain publish Push domain artifact" print " domain pull / Cache domain locally" print " domain describe / Show OCI manifest" print " domain verify / Check integrity + signature" print " domain diff

/:

/: Layer diff" print " ecosystem domains [--registry ] List all domain artifacts" print " ecosystem modes [--registry ] List all mode artifacts" print "" print " Mode lifecycle — subscribe, invoke, validate:" print " mode publish Push mode artifact" print " subscribe --mode-file --workspace-dir " print " [--registry ] [--ncl-import ]" print " Pull domain deps, verify signatures, scaffold cabling.ncl" print " invoke [--binary ] [--workspace-root ]" print " [--ncl-import ] [--dry-run]" print " Assemble context and pipe to mode binary stdin" print " validate --workspace-dir [--ncl-import ]" print " Typecheck cabling.ncl and check all bindings resolve" print " describe --mode-file [--ncl-import ]" print " Show mode metadata: domains, steps, direction" print " list [--workspace-dir ]" print " List cabling files and cached domains" print " list --live [--registry ]" print " Query live registry: domains/ + modes/ present in the registry" print "" print " Examples:" print " prvng i domain verify secret-delivery 0.1.0" print " prvng i domain publish catalog/domains/secret-delivery" print " prvng i ecosystem domains" print " prvng i subscribe lian-build --mode-file infra/modes/lian-build.ncl --workspace-dir ." print " prvng i invoke lian-build --dry-run" print " prvng i validate lian-build --workspace-dir ." print " prvng i list" print "" print " Flags:" print " --registry Override default registry (reg.librecloud.online)" print " --ncl-import Extra Nickel import path (defaults to \$PROVISIONING)" print " --workspace-root

Workspace root for cabling resolution (defaults to \$PWD)" print " --dry-run Print assembled context envelope without invoking binary" } # ── entry point ─────────────────────────────────────────────────────────────── def main [...args: string]: nothing -> nothing { let cmd = $args | get 0? | default "" let rest = if ($args | length) > 1 { $args | skip 1 } else { [] } match $cmd { "integration" | "i" => { let sub = $rest | get 0? | default "" let sub_rest = if ($rest | length) > 1 { $rest | skip 1 } else { [] } match $sub { "domain" => { cmd-domain $sub_rest } "mode" => { cmd-mode $sub_rest } "ecosystem" => { cmd-ecosystem $sub_rest } "invoke" => { cmd-invoke $sub_rest } "list" => { cmd-list $sub_rest } "describe" => { cmd-describe $sub_rest } "subscribe" => { cmd-subscribe $sub_rest } "validate" => { cmd-validate $sub_rest } _ => { _print-integration-help } } } _ => { _print-integration-help } } }