.PHONY: all build build-local build-edge build-edge-host build-node build-node-target build-node-targets pack-node-target pack-edge archive-edge tidy test test-e2e test-control-plane-edge-wire test-credential-slot-smoke test-openai-ollama test-openai-lemonade test-openai-glm-coding test-hot-path-agent-smoke-self-test test-hot-path-agent-smoke-preflight test-hot-path-agent-smoke readability-audit proto proto-dart client-test client-build-web clean GOFLAGS ?= -trimpath BUILD_DIR ?= build BUILD_BIN_DIR ?= $(BUILD_DIR)/bin ARTIFACT_DIR ?= $(BUILD_DIR)/artifacts PACK_DIR ?= $(BUILD_DIR)/packages EDGE_TARGET ?= $(shell go env GOOS)-$(shell go env GOARCH) EDGE_TARGET_PARTS = $(subst -, ,$(EDGE_TARGET)) EDGE_GOOS = $(word 1,$(EDGE_TARGET_PARTS)) EDGE_GOARCH = $(word 2,$(EDGE_TARGET_PARTS)) EDGE_PACKAGE_NAME ?= iop-edge-$(EDGE_TARGET) EDGE_PACKAGE ?= $(PACK_DIR)/$(EDGE_PACKAGE_NAME).tar.gz EDGE_PACKAGE_TMP ?= $(BUILD_DIR)/.edge-package EDGE_HOST_BINARY ?= $(BUILD_BIN_DIR)/iop-edge-host NODE_TARGET ?= $(shell go env GOOS)-$(shell go env GOARCH) NODE_TARGETS ?= linux-arm64 linux-amd64 darwin-arm64 darwin-amd64 windows-arm64 windows-amd64 NODE_TARGET_PARTS = $(subst -, ,$(NODE_TARGET)) NODE_GOOS = $(word 1,$(NODE_TARGET_PARTS)) NODE_GOARCH = $(word 2,$(NODE_TARGET_PARTS)) IOP_CONTROL_PLANE_HTTP_URL ?= http://localhost:18000 IOP_CONTROL_PLANE_WIRE_URL ?= ws://localhost:19080/client all: build build: build-node-targets $(MAKE) build-edge $(MAKE) archive-edge build-local: build-edge build-node build-edge: @test -n "$(EDGE_GOOS)" && test -n "$(EDGE_GOARCH)" || (echo "EDGE_TARGET must be -" >&2; exit 2) mkdir -p $(BUILD_BIN_DIR) GOOS=$(EDGE_GOOS) GOARCH=$(EDGE_GOARCH) go build $(GOFLAGS) -o $(BUILD_BIN_DIR)/iop-edge ./apps/edge/cmd/edge build-edge-host: mkdir -p $(BUILD_BIN_DIR) go build $(GOFLAGS) -o $(EDGE_HOST_BINARY) ./apps/edge/cmd/edge build-node: mkdir -p $(BUILD_BIN_DIR) go build $(GOFLAGS) -o $(BUILD_BIN_DIR)/iop-node ./apps/node/cmd/node build-node-target: @test -n "$(NODE_GOOS)" && test -n "$(NODE_GOARCH)" || (echo "NODE_TARGET must be -" >&2; exit 2) mkdir -p $(BUILD_BIN_DIR) GOOS=$(NODE_GOOS) GOARCH=$(NODE_GOARCH) go build $(GOFLAGS) -o $(BUILD_BIN_DIR)/iop-node-$(NODE_TARGET) ./apps/node/cmd/node build-node-targets: build-edge-host rm -rf "$(ARTIFACT_DIR)" @for target in $(NODE_TARGETS); do \ echo "==> node target $$target"; \ $(MAKE) build-node-target NODE_TARGET=$$target; \ $(EDGE_HOST_BINARY) bootstrap pack --target $$target --node-binary $(BUILD_BIN_DIR)/iop-node-$$target --output $(ARTIFACT_DIR); \ done pack-node-target: build-edge-host build-node-target $(EDGE_HOST_BINARY) bootstrap pack --target $(NODE_TARGET) --node-binary $(BUILD_BIN_DIR)/iop-node-$(NODE_TARGET) --output $(ARTIFACT_DIR) pack-edge: build-edge archive-edge archive-edge: @test -d "$(ARTIFACT_DIR)/bootstrap" || (echo "missing node bootstrap artifacts; run make pack-node-target NODE_TARGET=- first" >&2; exit 2) rm -rf "$(EDGE_PACKAGE_TMP)" mkdir -p "$(EDGE_PACKAGE_TMP)/$(EDGE_PACKAGE_NAME)/artifacts" "$(EDGE_PACKAGE_TMP)/$(EDGE_PACKAGE_NAME)/logs" "$(PACK_DIR)" cp "$(BUILD_BIN_DIR)/iop-edge" "$(EDGE_PACKAGE_TMP)/$(EDGE_PACKAGE_NAME)/iop-edge" cp -R "$(ARTIFACT_DIR)/." "$(EDGE_PACKAGE_TMP)/$(EDGE_PACKAGE_NAME)/artifacts/" tar -C "$(EDGE_PACKAGE_TMP)" -czf "$(EDGE_PACKAGE)" "$(EDGE_PACKAGE_NAME)" rm -rf "$(EDGE_PACKAGE_TMP)" @echo "wrote $(EDGE_PACKAGE)" tidy: go mod tidy test: go test ./... readability-audit: python3 scripts/readability_audit.py --check --input-mode worktree --output build/readability-audit.json test-e2e: @echo "NOTE: test-e2e runs auxiliary smoke (Edge-Node + OpenAI) plus Control Plane-Edge wire smoke; completion still requires user-flow verification when changing runtime paths." ./scripts/e2e-smoke.sh ./scripts/e2e-openai-ollama.sh ./scripts/e2e-control-plane-edge-wire.sh test-control-plane-edge-wire: ./scripts/e2e-control-plane-edge-wire.sh IOP_SECURE_DELIVERY_E2E=1 go test -count=1 -run '^TestSecureDeliveryThreeProcess$$' ./apps/control-plane/cmd/control-plane test-credential-slot-smoke: ./scripts/e2e-credential-slot-smoke.sh test-openai-ollama: ./scripts/e2e-openai-ollama.sh test-openai-lemonade: ./scripts/e2e-openai-lemonade.sh # Dedicated required diagnostic for the built-in glm_coding Coding Plan profile. # Deterministic, credential-free Edge -> Node -> loopback-provider full-cycle. # Reported separately from auxiliary test-e2e; intentionally not part of it. test-openai-glm-coding: ./scripts/e2e-openai-glm-coding.sh # Hot Path Claude/Pi agent smoke harness entry points # (scripts/e2e-hot-path-agents.sh). Three isolated targets keep credential-free # behavioral validation, external input preflight, and the credentialed two-agent # matrix separate. The credentialed matrix is reported separately and is # intentionally NOT part of test, test-e2e, or any aggregate local target. # # -self-test is credential-free and takes no variables; build it into local # verification. -preflight and -run forward caller-supplied variables only: no # secret, endpoint, config, or model value is read, defaulted, or serialized by # Make, and the harness never echoes one. The harness fingerprints the current # worktree and validates Edge/Pi/CLI runtime, base/profile and per-scenario alias # identity plus a live observation log before any agent invocation; any missing or # mismatched input causes the harness to exit 69 (GNU Make then reports the failed # recipe with process status 2 and `Error 69` in stderr). # # Required caller inputs include base/profile, direct/pass/repair/slow aliases, # Edge binary/config, Pi config dir, current runtime evidence, one live # observation log, disposable workspace/output, and secret env-var names. All are # caller-supplied with no defaults: # IOP_HOT_SMOKE_CLAUDE_BIN path to the claude runner binary # IOP_HOT_SMOKE_PI_BIN path to the pi runner binary # IOP_HOT_SMOKE_RUNTIME_EVIDENCE runtime identity evidence JSON (source/worktree # fingerprint + edge/pi/claude binary + config + # fixture + base/profile + alias digests) # IOP_HOT_SMOKE_BASE_URL IOP Hot Path base URL (bound to Claude via env) # IOP_HOT_SMOKE_DIRECT_MODEL preset alias for the direct scenario # IOP_HOT_SMOKE_PASS_MODEL preset alias for light-pass/write-unavailable # IOP_HOT_SMOKE_REPAIR_MODEL preset alias for the repair scenario # IOP_HOT_SMOKE_SLOW_MODEL preset alias for the timeout-cancel scenario # IOP_HOT_SMOKE_EDGE_BIN path to the selected IOP Edge binary # IOP_HOT_SMOKE_EDGE_CONFIG path to the selected Edge config file # PI_CODING_AGENT_DIR Pi config dir (also exported to the pi child) # IOP_HOT_SMOKE_PI_PROVIDER pi provider name selecting the IOP preset # IOP_HOT_SMOKE_OBSERVATION_FILE live Edge log holding hot_path_observation JSON # IOP_HOT_SMOKE_WORKSPACE_PARENT disposable workspace parent dir # IOP_HOT_SMOKE_OUTPUT manifest output path # IOP_HOT_SMOKE_CLAUDE_SECRET_ENV name of the env var holding the claude secret # IOP_HOT_SMOKE_PI_SECRET_ENV name of the env var holding the pi secret # Optional variables (forwarded only when set): # IOP_HOT_SMOKE_FIXTURE fixture/schema path (defaults to harness schema) test-hot-path-agent-smoke-self-test: ./scripts/e2e-hot-path-agents.sh --self-test test-hot-path-agent-smoke-preflight: ./scripts/e2e-hot-path-agents.sh --preflight-only \ --claude "$(IOP_HOT_SMOKE_CLAUDE_BIN)" \ --pi "$(IOP_HOT_SMOKE_PI_BIN)" \ --runtime-evidence "$(IOP_HOT_SMOKE_RUNTIME_EVIDENCE)" \ --base-url "$(IOP_HOT_SMOKE_BASE_URL)" \ --direct-model "$(IOP_HOT_SMOKE_DIRECT_MODEL)" \ --pass-model "$(IOP_HOT_SMOKE_PASS_MODEL)" \ --repair-model "$(IOP_HOT_SMOKE_REPAIR_MODEL)" \ --slow-model "$(IOP_HOT_SMOKE_SLOW_MODEL)" \ --edge-bin "$(IOP_HOT_SMOKE_EDGE_BIN)" \ --edge-config "$(IOP_HOT_SMOKE_EDGE_CONFIG)" \ --pi-config-dir "$(PI_CODING_AGENT_DIR)" \ --pi-provider "$(IOP_HOT_SMOKE_PI_PROVIDER)" \ --observation-file "$(IOP_HOT_SMOKE_OBSERVATION_FILE)" \ --workspace-root "$(IOP_HOT_SMOKE_WORKSPACE_PARENT)" \ --output "$(IOP_HOT_SMOKE_OUTPUT)" \ --claude-secret-env "$(IOP_HOT_SMOKE_CLAUDE_SECRET_ENV)" \ --pi-secret-env "$(IOP_HOT_SMOKE_PI_SECRET_ENV)" \ $(if $(IOP_HOT_SMOKE_FIXTURE),--fixture "$(IOP_HOT_SMOKE_FIXTURE)") test-hot-path-agent-smoke: ./scripts/e2e-hot-path-agents.sh --run \ --claude "$(IOP_HOT_SMOKE_CLAUDE_BIN)" \ --pi "$(IOP_HOT_SMOKE_PI_BIN)" \ --runtime-evidence "$(IOP_HOT_SMOKE_RUNTIME_EVIDENCE)" \ --base-url "$(IOP_HOT_SMOKE_BASE_URL)" \ --direct-model "$(IOP_HOT_SMOKE_DIRECT_MODEL)" \ --pass-model "$(IOP_HOT_SMOKE_PASS_MODEL)" \ --repair-model "$(IOP_HOT_SMOKE_REPAIR_MODEL)" \ --slow-model "$(IOP_HOT_SMOKE_SLOW_MODEL)" \ --edge-bin "$(IOP_HOT_SMOKE_EDGE_BIN)" \ --edge-config "$(IOP_HOT_SMOKE_EDGE_CONFIG)" \ --pi-config-dir "$(PI_CODING_AGENT_DIR)" \ --pi-provider "$(IOP_HOT_SMOKE_PI_PROVIDER)" \ --observation-file "$(IOP_HOT_SMOKE_OBSERVATION_FILE)" \ --workspace-root "$(IOP_HOT_SMOKE_WORKSPACE_PARENT)" \ --output "$(IOP_HOT_SMOKE_OUTPUT)" \ --claude-secret-env "$(IOP_HOT_SMOKE_CLAUDE_SECRET_ENV)" \ --pi-secret-env "$(IOP_HOT_SMOKE_PI_SECRET_ENV)" \ $(if $(IOP_HOT_SMOKE_FIXTURE),--fixture "$(IOP_HOT_SMOKE_FIXTURE)") # Requires: protoc + protoc-gen-go (go install google.golang.org/protobuf/cmd/protoc-gen-go@latest) proto: protoc \ --go_out=. \ --go_opt=module=iop \ --proto_path=. \ proto/iop/runtime.proto \ proto/iop/node.proto \ proto/iop/control.proto \ proto/iop/job.proto # Try finding protoc-gen-dart in PATH or common fallback locations PROTOC_GEN_DART := $(shell which protoc-gen-dart 2>/dev/null) ifeq ($(PROTOC_GEN_DART),) PROTOC_GEN_DART := $(wildcard $(HOME)/.pub-cache/bin/protoc-gen-dart) endif ifeq ($(PROTOC_GEN_DART),) PROTOC_GEN_DART := $(wildcard /config/.pub-cache/bin/protoc-gen-dart) endif proto-dart: ifeq ($(PROTOC_GEN_DART),) @echo "Error: protoc-gen-dart not found." @echo "Please install it by running: flutter pub global activate protoc_plugin" @exit 1 endif mkdir -p apps/client/lib/gen protoc \ --plugin=protoc-gen-dart=$(PROTOC_GEN_DART) \ --dart_out=apps/client/lib/gen \ --proto_path=. \ --proto_path=/config/.local/include \ proto/iop/runtime.proto \ proto/iop/node.proto \ proto/iop/control.proto \ proto/iop/job.proto client-test: cd apps/client && flutter test client-build-web: cd apps/client && flutter build web \ --dart-define=IOP_CONTROL_PLANE_HTTP_URL=$(IOP_CONTROL_PLANE_HTTP_URL) \ --dart-define=IOP_CONTROL_PLANE_WIRE_URL=$(IOP_CONTROL_PLANE_WIRE_URL) clean: rm -rf build rm -rf dist rm -f iop.db rm -f build/readability-audit.json