diff --git a/apps/client/lib/client_bootstrap.dart b/apps/client/lib/client_bootstrap.dart index 40c503b..ee2d898 100644 --- a/apps/client/lib/client_bootstrap.dart +++ b/apps/client/lib/client_bootstrap.dart @@ -1,4 +1,5 @@ import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'main.dart'; import 'src/integrations/mattermost/mattermost_push_host_integration.dart'; @@ -16,18 +17,18 @@ class IopClientBootstrapOptions { }); } -final MattermostPushHostIntegration defaultMattermostHost = MattermostPushHostIntegration( - pushClient: MattermostPushPluginClient(), -); +final MattermostPushHostIntegration defaultMattermostHost = + MattermostPushHostIntegration(pushClient: MattermostPushPluginClient()); Future bootstrapIopClient({ IopClientBootstrapOptions options = const IopClientBootstrapOptions(), }) async { applyFullscreenMode(); - if (options.initializeFirebase) { + final initializeExternalPush = !kIsWeb; + if (initializeExternalPush && options.initializeFirebase) { await Firebase.initializeApp(); } - if (options.initializeMattermost) { + if (initializeExternalPush && options.initializeMattermost) { final host = options.mattermostHost ?? defaultMattermostHost; await host.initialize(); } @@ -38,9 +39,12 @@ Future runIopClient({ }) async { WidgetsFlutterBinding.ensureInitialized(); await bootstrapIopClient(options: options); - runApp(IopClientApp( - mattermostHost: options.initializeMattermost - ? (options.mattermostHost ?? defaultMattermostHost) - : null, - )); + final initializeExternalPush = !kIsWeb; + runApp( + IopClientApp( + mattermostHost: initializeExternalPush && options.initializeMattermost + ? (options.mattermostHost ?? defaultMattermostHost) + : null, + ), + ); } diff --git a/apps/edge/README.md b/apps/edge/README.md index a1f6fe5..1ab2674 100644 --- a/apps/edge/README.md +++ b/apps/edge/README.md @@ -185,7 +185,7 @@ nodes: adapters: ollama: enabled: true - base_url: "http://192.168.0.91:11434" + base_url: "http://ollama-host.local:11434" context_size: 262144 ``` diff --git a/docker-compose.yml b/docker-compose.yml index 09f2104..8159c07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,9 +42,9 @@ services: IOP_REDIS_URL: "redis://redis:6379/${IOP_REDIS_DB:-2}" IOP_REDIS_KEY_PREFIX: "${IOP_REDIS_KEY_PREFIX:-iop:control-plane:dev:}" ports: - - "${IOP_CONTROL_PLANE_HTTP_BIND:-127.0.0.1}:${IOP_CONTROL_PLANE_HTTP_PORT:-18000}:18000" + - "${IOP_CONTROL_PLANE_HTTP_BIND:-0.0.0.0}:${IOP_CONTROL_PLANE_HTTP_PORT:-18000}:18000" - "${IOP_CONTROL_PLANE_WIRE_BIND:-0.0.0.0}:${IOP_CONTROL_PLANE_WIRE_PORT:-19080}:19080" - - "${IOP_CONTROL_PLANE_EDGE_WIRE_BIND:-127.0.0.1}:${IOP_CONTROL_PLANE_EDGE_WIRE_PORT:-19081}:19081" + - "${IOP_CONTROL_PLANE_EDGE_WIRE_BIND:-0.0.0.0}:${IOP_CONTROL_PLANE_EDGE_WIRE_PORT:-19081}:19081" depends_on: postgres: condition: service_healthy @@ -68,7 +68,7 @@ services: IOP_CONTROL_PLANE_EDGE_WIRE_ADDR: "control-plane:19081" IOP_EDGE_NODE_ID: "${IOP_EDGE_NODE_ID:-external-node}" IOP_EDGE_NODE_ALIAS: "${IOP_EDGE_NODE_ALIAS:-external-node}" - IOP_EDGE_NODE_TOKEN: "${IOP_EDGE_NODE_TOKEN:?set IOP_EDGE_NODE_TOKEN for external node access}" + IOP_EDGE_NODE_TOKEN: "${IOP_EDGE_NODE_TOKEN:-dev-node-token-change-me}" ports: - "${IOP_EDGE_NODE_BIND:-0.0.0.0}:${IOP_EDGE_NODE_PORT:-19090}:9090" depends_on: @@ -92,3 +92,10 @@ services: volumes: control-plane-postgres: control-plane-redis: + +networks: + default: + name: iop-dev-net + ipam: + config: + - subnet: 10.89.0.0/24 diff --git a/docs/edge-local-dev-guide.md b/docs/edge-local-dev-guide.md index deb634e..e2f4f43 100644 --- a/docs/edge-local-dev-guide.md +++ b/docs/edge-local-dev-guide.md @@ -1,120 +1,117 @@ # Edge Local Quickstart -Edge는 package 하나, Node는 bootstrap 한 줄로 설치한다. -이 문서는 Control Plane 없이 Edge host에서 bootstrap, local config, 진단, 단일 Edge 유지보수를 수행하는 Edge-local 경로다. 여러 Edge를 상시 관찰하거나 정책/감사/팀 운영을 수행하는 기본 운영면은 후속 Control Plane으로 둔다. +이 문서는 `toki-labs.com` Control Plane에 native Edge를 붙이고, Ollama Node를 연결해 OpenAI-compatible smoke까지 확인하는 최소 절차다. -`scripts/dev/*` helper는 repo 내부 개발 진단용이다. field 기본 경로는 `iop-edge` package와 여기서 출력되는 one-line bootstrap command다. +설정 기준은 `edge.yaml`이다. 주소는 환경 변수로 흩뿌리지 않는다. ## 1. Package +현재 `toki-labs.com` 원격처럼 Apple Silicon macOS에서 실행한다. + ```bash make build ``` -결과: `build/packages/iop-edge--.tar.gz` - -## 2. Edge +## 2. Edge 설정 ```bash -tar -xzf iop-edge--.tar.gz -C /opt -cd /opt/iop-edge-- - +rm -rf "$HOME/iop-edge-test" +mkdir -p "$HOME/iop-edge-test" +tar -xzf build/packages/iop-edge-darwin-arm64.tar.gz -C "$HOME/iop-edge-test" +cd "$HOME/iop-edge-test/iop-edge-darwin-arm64" ./iop-edge config init -./iop-edge env ``` -필요하면 `edge.yaml`의 host/port/model만 수정한다. +`edge.yaml`에서 아래 값만 맞춘다. 모델만 필요하면 바꾼다. + +```yaml +edge: + id: "edge-toki-labs" + name: "Toki Labs Edge" + +server: + listen: "0.0.0.0:19090" + advertise_host: "toki-labs.com" + +bootstrap: + listen: "0.0.0.0:18080" + artifact_base_url: "http://toki-labs.com:18080" + artifact_dir: "artifacts" + +control_plane: + enabled: true + wire_addr: "toki-labs.com:19081" + +openai: + enabled: true + listen: "0.0.0.0:18081" + adapter: "ollama" + target: "gemma4:26b" + models: + - "gemma4:26b" + timeout_sec: 300 + +nodes: [] +``` + +확인: + +```bash +./iop-edge --config edge.yaml env +./iop-edge --config edge.yaml config check +``` ## 3. Node 등록 ```bash -./iop-edge node register node-1 -``` - -Ollama Node: - -```bash -./iop-edge node register node-1 \ +./iop-edge --config edge.yaml node register node-ollama-1 \ --adapter ollama \ --ollama-base-url http://127.0.0.1:11434 ``` -출력된 `curl ... | bash -s ` 한 줄을 복사한다. +출력된 `curl -fsSL ... | bash -s ` 명령을 보관한다. ## 4. Edge 실행 ```bash -./iop-edge config check -./iop-edge serve +mkdir -p logs run +nohup ./iop-edge --config edge.yaml serve > logs/edge.stdout.log 2>&1 & +echo $! > run/iop-edge.pid ``` -서비스 실행: +확인: ```bash -sudo ./iop-edge setup --enable --start +curl -fsS http://toki-labs.com:18000/edges ``` -## 5. Node 설치 +## 5. Node 실행 -Node host에서 복사한 명령을 실행한다. +3단계에서 출력된 bootstrap 명령을 Node host에서 실행한다. ```bash -curl -fsSL http://:18080/bootstrap/node.sh | bash -s +curl -fsSL http://toki-labs.com:18080/bootstrap/node.sh | bash -s ``` -`node.sh`가 OS/architecture를 자동 감지한다. - -## 6. 확인 +Node 연결 확인: ```bash -tail -f logs/edge.log -./iop-edge nodes list +curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status ``` -정상: `node registered` - -## 운영 범위 - -- **Edge-local 필수**: package 설치, `edge.yaml` 생성/검증, effective environment 확인, node bootstrap command 발급, 단일 Edge smoke와 복구 진단 -- **Control Plane 기본**: 여러 Edge 상태 조회, 팀 운영 UI, 정책/권한/audit, 반복 운영/리포트, fleet-wide command -- **Shared operation**: Edge 상태, node 및 후속 agent 등록 상태, command/event, run/cancel/status, bootstrap 발급 상태처럼 CLI와 Control Plane이 모두 다룰 수 있는 작업 - -새 command가 필요하면 먼저 이 셋 중 하나로 분류한다. Shared operation은 CLI와 Control Plane이 각각 별도 로직을 갖지 않고 Edge의 공통 operation/service 경계를 통해 노출한다. - -## Build Options +## 6. Smoke ```bash -make build -make build EDGE_TARGET=darwin-arm64 -make build EDGE_TARGET=darwin-arm64 NODE_TARGETS="darwin-arm64" -make pack-node-target NODE_TARGET=darwin-arm64 && make pack-edge EDGE_TARGET=darwin-arm64 +curl -fsS http://toki-labs.com:18081/v1/models + +./iop-edge --config edge.yaml smoke openai \ + --model gemma4:26b \ + --base-url http://toki-labs.com:18081 \ + --timeout 60s ``` -`EDGE_TARGET`는 package에 들어갈 Edge binary와 Edge package 이름을 정한다. -`NODE_TARGETS`/`NODE_TARGET`는 Node bootstrap artifact 대상을 정한다. +외부 OpenAI-compatible client base URL: -Apple Silicon Mac: - -```bash -make build EDGE_TARGET=darwin-arm64 -``` - -Intel Mac: - -```bash -make build EDGE_TARGET=darwin-amd64 -``` - -Node bootstrap artifact도 macOS용만 포함하려면 `NODE_TARGETS`를 함께 좁힌다. - -```bash -make build EDGE_TARGET=darwin-arm64 NODE_TARGETS="darwin-arm64" -make build EDGE_TARGET=darwin-amd64 NODE_TARGETS="darwin-amd64" -``` - -결과 예시: - -```bash -build/packages/iop-edge-darwin-arm64.tar.gz -build/packages/iop-edge-darwin-amd64.tar.gz +```text +http://toki-labs.com:18081/v1 ```