feat(useragent): added connection info setup screen
This commit is contained in:
128
AGENTS.md
Normal file
128
AGENTS.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Arbiter is a **permissioned signing service** for cryptocurrency wallets. It consists of:
|
||||
- **`server/`** — Rust gRPC daemon that holds encrypted keys and enforces policies
|
||||
- **`useragent/`** — Flutter desktop app (macOS/Windows) with a Rust backend via Rinf
|
||||
- **`protobufs/`** — Protocol Buffer definitions shared between server and client
|
||||
|
||||
The vault never exposes key material; it only produces signatures when requests satisfy configured policies.
|
||||
|
||||
## Toolchain Setup
|
||||
|
||||
Tools are managed via [mise](https://mise.jdx.dev/). Install all required tools:
|
||||
```sh
|
||||
mise install
|
||||
```
|
||||
|
||||
Key versions: Rust 1.93.0 (with clippy), Flutter 3.38.9-stable, protoc 29.6, diesel_cli 2.3.6 (sqlite).
|
||||
|
||||
## Server (Rust workspace at `server/`)
|
||||
|
||||
### Crates
|
||||
|
||||
| Crate | Purpose |
|
||||
|---|---|
|
||||
| `arbiter-proto` | Generated gRPC stubs + protobuf types; compiled from `protobufs/*.proto` via `tonic-prost-build` |
|
||||
| `arbiter-server` | Main daemon — actors, DB, EVM policy engine, gRPC service implementation |
|
||||
| `arbiter-useragent` | Rust client library for the user agent side of the gRPC protocol |
|
||||
| `arbiter-client` | Rust client library for SDK clients |
|
||||
|
||||
### Common Commands
|
||||
|
||||
```sh
|
||||
cd server
|
||||
|
||||
# Build
|
||||
cargo build
|
||||
|
||||
# Run the server daemon
|
||||
cargo run -p arbiter-server
|
||||
|
||||
# Run all tests (preferred over cargo test)
|
||||
cargo nextest run
|
||||
|
||||
# Run a single test
|
||||
cargo nextest run <test_name>
|
||||
|
||||
# Lint
|
||||
cargo clippy
|
||||
|
||||
# Security audit
|
||||
cargo audit
|
||||
|
||||
# Check unused dependencies
|
||||
cargo shear
|
||||
|
||||
# Run snapshot tests and update snapshots
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
### Architecture
|
||||
|
||||
The server is actor-based using the **kameo** crate. All long-lived state lives in `GlobalActors`:
|
||||
|
||||
- **`Bootstrapper`** — Manages the one-time bootstrap token written to `~/.arbiter/bootstrap_token` on first run.
|
||||
- **`KeyHolder`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell.
|
||||
- **`MessageRouter`** — Coordinates streaming messages between user agents and SDK clients.
|
||||
- **`EvmActor`** — Handles EVM transaction policy enforcement and signing.
|
||||
|
||||
Per-connection actors live under `actors/user_agent/` and `actors/client/`, each with `auth` (challenge-response authentication) and `session` (post-auth operations) sub-modules.
|
||||
|
||||
**Database:** SQLite via `diesel-async` + `bb8` connection pool. Schema managed by embedded Diesel migrations in `crates/arbiter-server/migrations/`. DB file lives at `~/.arbiter/arbiter.sqlite`. Tests use a temp-file DB via `db::create_test_pool()`.
|
||||
|
||||
**Cryptography:**
|
||||
- Authentication: ed25519 (challenge-response, nonce-tracked per peer)
|
||||
- Encryption at rest: XChaCha20-Poly1305 (versioned via `scheme` field for transparent migration on unseal)
|
||||
- Password KDF: Argon2
|
||||
- Unseal transport: X25519 ephemeral key exchange
|
||||
- TLS: self-signed certificate (aws-lc-rs backend), fingerprint distributed via `ArbiterUrl`
|
||||
|
||||
**Protocol:** gRPC with Protocol Buffers. The `ArbiterUrl` type encodes host, port, CA cert, and bootstrap token into a single shareable string (printed to console on first run).
|
||||
|
||||
### Proto Regeneration
|
||||
|
||||
When `.proto` files in `protobufs/` change, rebuild to regenerate:
|
||||
```sh
|
||||
cd server && cargo build -p arbiter-proto
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
|
||||
```sh
|
||||
# Create a new migration
|
||||
diesel migration generate <name> --migration-dir crates/arbiter-server/migrations
|
||||
|
||||
# Run migrations manually (server also runs them on startup)
|
||||
diesel migration run --migration-dir crates/arbiter-server/migrations
|
||||
```
|
||||
|
||||
## User Agent (Flutter + Rinf at `useragent/`)
|
||||
|
||||
The Flutter app uses [Rinf](https://rinf.cunarist.org) to call Rust code. The Rust logic lives in `useragent/native/hub/` as a separate crate that uses `arbiter-useragent` for the gRPC client.
|
||||
|
||||
Communication between Dart and Rust uses typed **signals** defined in `useragent/native/hub/src/signals/`. After modifying signal structs, regenerate Dart bindings:
|
||||
|
||||
```sh
|
||||
cd useragent && rinf gen
|
||||
```
|
||||
|
||||
### Common Commands
|
||||
|
||||
```sh
|
||||
cd useragent
|
||||
|
||||
# Run the app (macOS or Windows)
|
||||
flutter run
|
||||
|
||||
# Regenerate Rust↔Dart signal bindings
|
||||
rinf gen
|
||||
|
||||
# Analyze Dart code
|
||||
flutter analyze
|
||||
```
|
||||
|
||||
The Rinf Rust entry point is `useragent/native/hub/src/lib.rs`. It spawns actors defined in `useragent/native/hub/src/actors/` which handle Dart↔server communication via signals.
|
||||
@@ -1,904 +0,0 @@
|
||||
{
|
||||
"configVersion": 2,
|
||||
"packages": [
|
||||
{
|
||||
"name": "_fe_analyzer_shared",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-91.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "analyzer",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/analyzer-8.4.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "analyzer_buffer",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/analyzer_buffer-0.1.11",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "ansicolor",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/ansicolor-2.0.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/args-2.7.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "async",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/async-2.13.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "auto_route",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/auto_route-11.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "auto_route_generator",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/auto_route_generator-10.4.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "biometric_signature",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/biometric_signature-10.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "bloc",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/bloc-9.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.14"
|
||||
},
|
||||
{
|
||||
"name": "boolean_selector",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "build",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/build-4.0.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "build_config",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/build_config-1.3.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "build_daemon",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/build_daemon-4.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "build_runner",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/build_runner-2.12.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "built_collection",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/built_collection-5.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "built_value",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/built_value-8.12.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "characters",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/characters-1.4.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "checked_yaml",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/checked_yaml-2.0.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "cli_config",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/cli_config-0.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "clock",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/clock-1.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "code_assets",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/code_assets-1.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "code_builder",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/code_builder-4.11.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "collection",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/collection-1.19.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "convert",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/convert-3.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "coverage",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/coverage-1.15.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "cross_file",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/cross_file-0.3.5+2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "crypto",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/crypto-3.0.7",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "cryptography",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/cryptography-2.9.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "cryptography_flutter",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/cryptography_flutter-2.3.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "cupertino_icons",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "dart_style",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/dart_style-3.1.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "fake_async",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/fake_async-1.3.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "ffi",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/ffi-2.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "file",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/file-7.0.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "fixnum",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/fixnum-1.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "flutter",
|
||||
"rootUri": "file:///Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable/packages/flutter",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "flutter_adaptive_scaffold",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/git/flutter_adaptive_scaffold-b2e3615901a7ab837cb7fc35efbfcf8b55f27638/",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "flutter_bloc",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_bloc-9.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.14"
|
||||
},
|
||||
{
|
||||
"name": "flutter_hooks",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_hooks-0.21.3+1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.17"
|
||||
},
|
||||
{
|
||||
"name": "flutter_lints",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_lints-6.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "flutter_riverpod",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_riverpod-3.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage-10.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage_darwin",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage_darwin-0.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage_linux",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-3.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage_platform_interface",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage_platform_interface-2.0.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage_web",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-2.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_secure_storage_windows",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-4.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "flutter_spinkit",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/flutter_spinkit-5.2.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "flutter_test",
|
||||
"rootUri": "file:///Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable/packages/flutter_test",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "flutter_web_plugins",
|
||||
"rootUri": "file:///Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable/packages/flutter_web_plugins",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "freezed_annotation",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/freezed_annotation-3.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "frontend_server_client",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "glob",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/glob-2.1.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "google_identity_services_web",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/google_identity_services_web-0.3.3+1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "googleapis_auth",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/googleapis_auth-2.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "graphs",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/graphs-2.3.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "group_button",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/group_button-5.3.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "grpc",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/grpc-5.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "hooks",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/hooks-1.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "hooks_riverpod",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/hooks_riverpod-3.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "hotreloader",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/hotreloader-4.3.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "http",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/http-1.6.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "http2",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/http2-2.3.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "http_multi_server",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "http_parser",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/http_parser-4.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "io",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/io-1.0.5",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "js",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/js-0.7.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "json_annotation",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/json_annotation-4.11.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "leak_tracker",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/leak_tracker-11.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "leak_tracker_flutter_testing",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "leak_tracker_testing",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "lean_builder",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/lean_builder-0.1.6",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "lints",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/lints-6.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "logging",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/logging-1.3.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "matcher",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/matcher-0.12.17",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "material_color_utilities",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.17"
|
||||
},
|
||||
{
|
||||
"name": "meta",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/meta-1.17.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "mime",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/mime-2.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "mockito",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/mockito-5.6.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "mtcore",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/git.markettakers.org%2547api%2547packages%2547MarketTakers%2547pub%2547/mtcore-1.0.6",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "native_toolchain_c",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/native_toolchain_c-0.17.5",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "nested",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/nested-1.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "node_preamble",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/node_preamble-2.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "objective_c",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/objective_c-9.3.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "package_config",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/package_config-2.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path-1.9.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "path_provider",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider-2.1.5",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_android",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_foundation",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider_foundation-2.6.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_linux",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.19"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_platform_interface",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "path_provider_windows",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "percent_indicator",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/percent_indicator-4.2.5",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/platform-3.1.6",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.2"
|
||||
},
|
||||
{
|
||||
"name": "plugin_platform_interface",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "pool",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/pool-1.5.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "protobuf",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/protobuf-6.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/provider-6.1.5+1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "pub_semver",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "pubspec_parse",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "rive",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/rive-0.14.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "rive_native",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/rive_native-0.1.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "riverpod",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/riverpod-3.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "riverpod_analyzer_utils",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/riverpod_analyzer_utils-1.0.0-dev.8",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "riverpod_annotation",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/riverpod_annotation-4.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "riverpod_generator",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/riverpod_generator-4.0.0+1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.7"
|
||||
},
|
||||
{
|
||||
"name": "share_plus",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/share_plus-12.0.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "share_plus_platform_interface",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/share_plus_platform_interface-6.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.18"
|
||||
},
|
||||
{
|
||||
"name": "shelf",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/shelf-1.4.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "shelf_packages_handler",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.17"
|
||||
},
|
||||
{
|
||||
"name": "shelf_static",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/shelf_static-1.1.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "shelf_web_socket",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "sizer",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/sizer-3.1.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "sky_engine",
|
||||
"rootUri": "file:///Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable/bin/cache/pkg/sky_engine",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "source_gen",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/source_gen-4.2.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.9"
|
||||
},
|
||||
{
|
||||
"name": "source_map_stack_trace",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "source_maps",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/source_maps-0.10.13",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "source_span",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/source_span-1.10.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "stack_trace",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/stack_trace-1.12.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "state_notifier",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/state_notifier-1.0.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.12"
|
||||
},
|
||||
{
|
||||
"name": "stream_channel",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/stream_channel-2.1.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "stream_transform",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/stream_transform-2.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "string_scanner",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/string_scanner-1.4.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "talker",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/talker-5.1.15",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.17"
|
||||
},
|
||||
{
|
||||
"name": "talker_flutter",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/talker_flutter-5.1.15",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.6"
|
||||
},
|
||||
{
|
||||
"name": "talker_logger",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/talker_logger-5.1.15",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.15"
|
||||
},
|
||||
{
|
||||
"name": "term_glyph",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/term_glyph-1.2.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "test",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/test-1.26.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "test_api",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/test_api-0.7.7",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "test_core",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/test_core-0.6.12",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "typed_data",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/typed_data-1.4.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "url_launcher_linux",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "url_launcher_platform_interface",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/url_launcher_platform_interface-2.3.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "url_launcher_web",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
},
|
||||
{
|
||||
"name": "url_launcher_windows",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "uuid",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/uuid-4.5.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "vector_math",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/vector_math-2.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "vm_service",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/vm_service-15.0.2",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.5"
|
||||
},
|
||||
{
|
||||
"name": "watcher",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/watcher-1.1.4",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.1"
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/web-1.1.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "web_socket",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/web_socket-1.0.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "web_socket_channel",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "webkit_inspection_protocol",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.0"
|
||||
},
|
||||
{
|
||||
"name": "win32",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/win32-5.15.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.8"
|
||||
},
|
||||
{
|
||||
"name": "xdg_directories",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.3"
|
||||
},
|
||||
{
|
||||
"name": "xxh3",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/xxh3-1.2.0",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "2.16"
|
||||
},
|
||||
{
|
||||
"name": "yaml",
|
||||
"rootUri": "file:///Users/kaska/.pub-cache/hosted/pub.dev/yaml-3.1.3",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.4"
|
||||
},
|
||||
{
|
||||
"name": "arbiter",
|
||||
"rootUri": "../",
|
||||
"packageUri": "lib/",
|
||||
"languageVersion": "3.10"
|
||||
}
|
||||
],
|
||||
"generator": "pub",
|
||||
"generatorVersion": "3.10.8",
|
||||
"flutterRoot": "file:///Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable",
|
||||
"flutterVersion": "3.38.9",
|
||||
"pubCache": "file:///Users/kaska/.pub-cache"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
3.38.9
|
||||
56
useragent/lib/features/arbiter_url.dart
Normal file
56
useragent/lib/features/arbiter_url.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class ArbiterUrl {
|
||||
const ArbiterUrl({
|
||||
required this.host,
|
||||
required this.port,
|
||||
required this.caCert,
|
||||
this.bootstrapToken,
|
||||
});
|
||||
|
||||
final String host;
|
||||
final int port;
|
||||
final List<int> caCert;
|
||||
final String? bootstrapToken;
|
||||
|
||||
static const _scheme = 'arbiter';
|
||||
static const _certQueryKey = 'cert';
|
||||
static const _bootstrapTokenQueryKey = 'bootstrap_token';
|
||||
|
||||
static ArbiterUrl parse(String value) {
|
||||
final uri = Uri.tryParse(value);
|
||||
if (uri == null || uri.scheme != _scheme) {
|
||||
throw const FormatException("Invalid URL scheme, expected 'arbiter://'");
|
||||
}
|
||||
|
||||
if (uri.host.isEmpty) {
|
||||
throw const FormatException('Missing host in URL');
|
||||
}
|
||||
|
||||
if (!uri.hasPort) {
|
||||
throw const FormatException('Missing port in URL');
|
||||
}
|
||||
|
||||
final cert = uri.queryParameters[_certQueryKey];
|
||||
if (cert == null || cert.isEmpty) {
|
||||
throw const FormatException("Missing 'cert' query parameter in URL");
|
||||
}
|
||||
|
||||
final decodedCert = _decodeCert(cert);
|
||||
|
||||
return ArbiterUrl(
|
||||
host: uri.host,
|
||||
port: uri.port,
|
||||
caCert: decodedCert,
|
||||
bootstrapToken: uri.queryParameters[_bootstrapTokenQueryKey],
|
||||
);
|
||||
}
|
||||
|
||||
static List<int> _decodeCert(String cert) {
|
||||
try {
|
||||
return base64Url.decode(base64Url.normalize(cert));
|
||||
} on FormatException catch (error) {
|
||||
throw FormatException("Invalid base64 in 'cert' query parameter: ${error.message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
3
useragent/lib/features/connection/connection.dart
Normal file
3
useragent/lib/features/connection/connection.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
class Connection {}
|
||||
@@ -1,6 +1,3 @@
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
enum KeyAlgorithm {
|
||||
rsa, ecdsa, ed25519
|
||||
}
|
||||
|
||||
67
useragent/lib/features/server_info_storage.dart
Normal file
67
useragent/lib/features/server_info_storage.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
class StoredServerInfo {
|
||||
const StoredServerInfo({
|
||||
required this.address,
|
||||
required this.port,
|
||||
required this.caCertFingerprint,
|
||||
});
|
||||
|
||||
final String address;
|
||||
final int port;
|
||||
final String caCertFingerprint;
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'address': address,
|
||||
'port': port,
|
||||
'caCertFingerprint': caCertFingerprint,
|
||||
};
|
||||
|
||||
factory StoredServerInfo.fromJson(Map<String, dynamic> json) {
|
||||
return StoredServerInfo(
|
||||
address: json['address'] as String,
|
||||
port: json['port'] as int,
|
||||
caCertFingerprint: json['caCertFingerprint'] as String,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ServerInfoStorage {
|
||||
Future<StoredServerInfo?> load();
|
||||
Future<void> save(StoredServerInfo serverInfo);
|
||||
Future<void> clear();
|
||||
}
|
||||
|
||||
class SecureServerInfoStorage implements ServerInfoStorage {
|
||||
static const _storageKey = 'server_info';
|
||||
|
||||
const SecureServerInfoStorage();
|
||||
|
||||
static const _storage = FlutterSecureStorage();
|
||||
|
||||
@override
|
||||
Future<StoredServerInfo?> load() async {
|
||||
final rawValue = await _storage.read(key: _storageKey);
|
||||
if (rawValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final decoded = jsonDecode(rawValue) as Map<String, dynamic>;
|
||||
return StoredServerInfo.fromJson(decoded);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save(StoredServerInfo serverInfo) {
|
||||
return _storage.write(
|
||||
key: _storageKey,
|
||||
value: jsonEncode(serverInfo.toJson()),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clear() {
|
||||
return _storage.delete(key: _storageKey);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,35 @@
|
||||
import 'package:arbiter/router.dart';
|
||||
import 'package:flutter/material.dart' hide Router;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(
|
||||
ProviderScope(
|
||||
child: MaterialApp.router(
|
||||
routerConfig: Router().config(),
|
||||
),
|
||||
),
|
||||
);
|
||||
runApp(const ProviderScope(child: App()));
|
||||
}
|
||||
|
||||
class App extends StatefulWidget {
|
||||
const App({super.key});
|
||||
|
||||
@override
|
||||
State<App> createState() => _AppState();
|
||||
}
|
||||
|
||||
class _AppState extends State<App> {
|
||||
late final Router _router;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_router = Router();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Sizer(
|
||||
builder: (context, orientation, deviceType) {
|
||||
return MaterialApp.router(routerConfig: _router.config());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
51
useragent/lib/providers/server_info.dart
Normal file
51
useragent/lib/providers/server_info.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:arbiter/features/server_info_storage.dart';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'server_info.g.dart';
|
||||
|
||||
@riverpod
|
||||
ServerInfoStorage serverInfoStorage(Ref ref) {
|
||||
return const SecureServerInfoStorage();
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class ServerInfo extends _$ServerInfo {
|
||||
@override
|
||||
Future<StoredServerInfo?> build() {
|
||||
final storage = ref.watch(serverInfoStorageProvider);
|
||||
return storage.load();
|
||||
}
|
||||
|
||||
Future<void> save({
|
||||
required String address,
|
||||
required int port,
|
||||
required List<int> caCert,
|
||||
}) async {
|
||||
final storage = ref.read(serverInfoStorageProvider);
|
||||
final fingerprint = await _fingerprint(caCert);
|
||||
final serverInfo = StoredServerInfo(
|
||||
address: address,
|
||||
port: port,
|
||||
caCertFingerprint: fingerprint,
|
||||
);
|
||||
|
||||
state = await AsyncValue.guard(() async {
|
||||
await storage.save(serverInfo);
|
||||
return serverInfo;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
final storage = ref.read(serverInfoStorageProvider);
|
||||
state = await AsyncValue.guard(() async {
|
||||
await storage.clear();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> _fingerprint(List<int> caCert) async {
|
||||
final digest = await Sha256().hash(caCert);
|
||||
return digest.bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
|
||||
}
|
||||
}
|
||||
102
useragent/lib/providers/server_info.g.dart
Normal file
102
useragent/lib/providers/server_info.g.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'server_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(serverInfoStorage)
|
||||
final serverInfoStorageProvider = ServerInfoStorageProvider._();
|
||||
|
||||
final class ServerInfoStorageProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
ServerInfoStorage,
|
||||
ServerInfoStorage,
|
||||
ServerInfoStorage
|
||||
>
|
||||
with $Provider<ServerInfoStorage> {
|
||||
ServerInfoStorageProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'serverInfoStorageProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$serverInfoStorageHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<ServerInfoStorage> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
ServerInfoStorage create(Ref ref) {
|
||||
return serverInfoStorage(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ServerInfoStorage value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ServerInfoStorage>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$serverInfoStorageHash() => r'fc06865e7314b1a2493c5de1a9347923a3d21c5c';
|
||||
|
||||
@ProviderFor(ServerInfo)
|
||||
final serverInfoProvider = ServerInfoProvider._();
|
||||
|
||||
final class ServerInfoProvider
|
||||
extends $AsyncNotifierProvider<ServerInfo, StoredServerInfo?> {
|
||||
ServerInfoProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'serverInfoProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$serverInfoHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ServerInfo create() => ServerInfo();
|
||||
}
|
||||
|
||||
String _$serverInfoHash() => r'6e94f52de03259695a2166b766004eec60ff45fa';
|
||||
|
||||
abstract class _$ServerInfo extends $AsyncNotifier<StoredServerInfo?> {
|
||||
FutureOr<StoredServerInfo?> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref =
|
||||
this.ref as $Ref<AsyncValue<StoredServerInfo?>, StoredServerInfo?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<StoredServerInfo?>, StoredServerInfo?>,
|
||||
AsyncValue<StoredServerInfo?>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
import 'package:arbiter/screens/dashboard/about.dart';
|
||||
import 'package:arbiter/screens/dashboard/calc.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
|
||||
import 'router.gr.dart';
|
||||
|
||||
@@ -11,6 +7,7 @@ class Router extends RootStackRouter {
|
||||
@override
|
||||
List<AutoRoute> get routes => [
|
||||
AutoRoute(page: Bootstrap.page, path: '/bootstrap', initial: true),
|
||||
AutoRoute(page: ServerInfoSetupRoute.page, path: '/server-info'),
|
||||
|
||||
AutoRoute(
|
||||
page: DashboardRouter.page,
|
||||
|
||||
@@ -13,33 +13,34 @@ import 'package:arbiter/screens/bootstrap.dart' as _i2;
|
||||
import 'package:arbiter/screens/dashboard.dart' as _i4;
|
||||
import 'package:arbiter/screens/dashboard/about.dart' as _i1;
|
||||
import 'package:arbiter/screens/dashboard/calc.dart' as _i3;
|
||||
import 'package:auto_route/auto_route.dart' as _i5;
|
||||
import 'package:arbiter/screens/server_info_setup.dart' as _i5;
|
||||
import 'package:auto_route/auto_route.dart' as _i6;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.AboutScreen]
|
||||
class AboutRoute extends _i5.PageRouteInfo<void> {
|
||||
const AboutRoute({List<_i5.PageRouteInfo>? children})
|
||||
class AboutRoute extends _i6.PageRouteInfo<void> {
|
||||
const AboutRoute({List<_i6.PageRouteInfo>? children})
|
||||
: super(AboutRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'AboutRoute';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return _i1.AboutScreen();
|
||||
return const _i1.AboutScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.Bootstrap]
|
||||
class Bootstrap extends _i5.PageRouteInfo<void> {
|
||||
const Bootstrap({List<_i5.PageRouteInfo>? children})
|
||||
class Bootstrap extends _i6.PageRouteInfo<void> {
|
||||
const Bootstrap({List<_i6.PageRouteInfo>? children})
|
||||
: super(Bootstrap.name, initialChildren: children);
|
||||
|
||||
static const String name = 'Bootstrap';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i2.Bootstrap();
|
||||
@@ -49,13 +50,13 @@ class Bootstrap extends _i5.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i3.CalcScreen]
|
||||
class CalcRoute extends _i5.PageRouteInfo<void> {
|
||||
const CalcRoute({List<_i5.PageRouteInfo>? children})
|
||||
class CalcRoute extends _i6.PageRouteInfo<void> {
|
||||
const CalcRoute({List<_i6.PageRouteInfo>? children})
|
||||
: super(CalcRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CalcRoute';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i3.CalcScreen();
|
||||
@@ -65,16 +66,32 @@ class CalcRoute extends _i5.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.DashboardRouter]
|
||||
class DashboardRouter extends _i5.PageRouteInfo<void> {
|
||||
const DashboardRouter({List<_i5.PageRouteInfo>? children})
|
||||
class DashboardRouter extends _i6.PageRouteInfo<void> {
|
||||
const DashboardRouter({List<_i6.PageRouteInfo>? children})
|
||||
: super(DashboardRouter.name, initialChildren: children);
|
||||
|
||||
static const String name = 'DashboardRouter';
|
||||
|
||||
static _i5.PageInfo page = _i5.PageInfo(
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i4.DashboardRouter();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.ServerInfoSetupScreen]
|
||||
class ServerInfoSetupRoute extends _i6.PageRouteInfo<void> {
|
||||
const ServerInfoSetupRoute({List<_i6.PageRouteInfo>? children})
|
||||
: super(ServerInfoSetupRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ServerInfoSetupRoute';
|
||||
|
||||
static _i6.PageInfo page = _i6.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i5.ServerInfoSetupScreen();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ class Bootstrap extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final container = ProviderScope.containerOf( context);
|
||||
final container = ProviderScope.containerOf(context);
|
||||
final completer = useMemoized(() {
|
||||
final completer = Completer<void>();
|
||||
completer.future.then((_) async {
|
||||
if (context.mounted) {
|
||||
final router = AutoRouter.of(context);
|
||||
router.replace(const DashboardRouter());
|
||||
router.replace(const ServerInfoSetupRoute());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mtcore/markettakers.dart' as mt;
|
||||
|
||||
|
||||
@RoutePage()
|
||||
class AboutScreen extends StatelessWidget {
|
||||
const AboutScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return mt.AboutScreen(decription: "Arbiter is bla bla bla");
|
||||
|
||||
284
useragent/lib/screens/server_info_setup.dart
Normal file
284
useragent/lib/screens/server_info_setup.dart
Normal file
@@ -0,0 +1,284 @@
|
||||
import 'package:arbiter/features/arbiter_url.dart';
|
||||
import 'package:arbiter/features/server_info_storage.dart';
|
||||
import 'package:arbiter/providers/server_info.dart';
|
||||
import 'package:arbiter/router.gr.dart';
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
@RoutePage()
|
||||
class ServerInfoSetupScreen extends HookConsumerWidget {
|
||||
const ServerInfoSetupScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final storedServerInfo = ref.watch(serverInfoProvider);
|
||||
final resolvedServerInfo = storedServerInfo.asData?.value;
|
||||
final controller = useTextEditingController();
|
||||
final errorText = useState<String?>(null);
|
||||
final isSaving = useState(false);
|
||||
|
||||
useEffect(() {
|
||||
final serverInfo = resolvedServerInfo;
|
||||
if (serverInfo != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
context.router.replace(const DashboardRouter());
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}, [context, resolvedServerInfo]);
|
||||
|
||||
Future<void> saveRemoteServerInfo() async {
|
||||
errorText.value = null;
|
||||
isSaving.value = true;
|
||||
|
||||
try {
|
||||
final arbiterUrl = ArbiterUrl.parse(controller.text.trim());
|
||||
await ref
|
||||
.read(serverInfoProvider.notifier)
|
||||
.save(
|
||||
address: arbiterUrl.host,
|
||||
port: arbiterUrl.port,
|
||||
caCert: arbiterUrl.caCert,
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
context.router.replace(const DashboardRouter());
|
||||
}
|
||||
} on FormatException catch (error) {
|
||||
errorText.value = error.message;
|
||||
} catch (_) {
|
||||
errorText.value = 'Failed to store connection settings.';
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (storedServerInfo.isLoading) {
|
||||
return const Scaffold(body: Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
|
||||
if (storedServerInfo.hasError) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Server Info Setup')),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6.w),
|
||||
child: Text(
|
||||
'Failed to load stored server info.',
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final serverInfo = resolvedServerInfo;
|
||||
if (serverInfo != null) {
|
||||
return _RedirectingView(serverInfo: serverInfo);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Server Info Setup')),
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final useRowLayout = constraints.maxWidth > constraints.maxHeight;
|
||||
final gap = 2.h;
|
||||
final horizontalPadding = 6.w;
|
||||
final verticalPadding = 3.h;
|
||||
final options = [
|
||||
const _OptionCard(
|
||||
title: 'Local',
|
||||
subtitle: 'Will start and connect to a local service in a future update.',
|
||||
enabled: false,
|
||||
child: SizedBox.shrink(),
|
||||
),
|
||||
_OptionCard(
|
||||
title: 'Remote',
|
||||
subtitle: 'Paste an Arbiter URL to store the server address, port, and CA fingerprint.',
|
||||
child: _RemoteConnectionForm(
|
||||
controller: controller,
|
||||
errorText: errorText.value,
|
||||
isSaving: isSaving.value,
|
||||
onSave: saveRemoteServerInfo,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
return ListView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: horizontalPadding,
|
||||
vertical: verticalPadding,
|
||||
),
|
||||
children: [
|
||||
_SetupHeader(gap: gap),
|
||||
SizedBox(height: gap),
|
||||
useRowLayout
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: options[0]),
|
||||
SizedBox(width: 3.w),
|
||||
Expanded(child: options[1]),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
options[0],
|
||||
SizedBox(height: gap),
|
||||
options[1],
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SetupHeader extends StatelessWidget {
|
||||
const _SetupHeader({required this.gap});
|
||||
|
||||
final double gap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Choose how this user agent should reach Arbiter.',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
SizedBox(height: gap * 0.5),
|
||||
Text(
|
||||
'Remote accepts the shareable Arbiter URL emitted by the server.',
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RedirectingView extends StatelessWidget {
|
||||
const _RedirectingView({required this.serverInfo});
|
||||
|
||||
final StoredServerInfo serverInfo;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
SizedBox(height: 2.h),
|
||||
Text('Using saved server ${serverInfo.address}:${serverInfo.port}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _RemoteConnectionForm extends StatelessWidget {
|
||||
const _RemoteConnectionForm({
|
||||
required this.controller,
|
||||
required this.errorText,
|
||||
required this.isSaving,
|
||||
required this.onSave,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String? errorText;
|
||||
final bool isSaving;
|
||||
final VoidCallback onSave;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Arbiter URL',
|
||||
hintText: 'arbiter://host:port?cert=...',
|
||||
),
|
||||
minLines: 2,
|
||||
maxLines: 4,
|
||||
),
|
||||
if (errorText != null) ...[
|
||||
SizedBox(height: 1.5.h),
|
||||
Text(
|
||||
errorText!,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 2.h),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: FilledButton(
|
||||
onPressed: isSaving ? null : onSave,
|
||||
child: Text(isSaving ? 'Saving...' : 'Save connection'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionCard extends StatelessWidget {
|
||||
const _OptionCard({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.child,
|
||||
this.enabled = true,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final Widget child;
|
||||
final bool enabled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(2.h),
|
||||
child: Opacity(
|
||||
opacity: enabled ? 1 : 0.55,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(title, style: theme.textTheme.titleLarge),
|
||||
if (!enabled) ...[
|
||||
SizedBox(width: 2.w),
|
||||
const Chip(label: Text('Coming soon')),
|
||||
],
|
||||
],
|
||||
),
|
||||
SizedBox(height: 1.h),
|
||||
Text(subtitle, style: theme.textTheme.bodyMedium),
|
||||
if (enabled) ...[
|
||||
SizedBox(height: 2.h),
|
||||
child,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=/Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable
|
||||
FLUTTER_APPLICATION_PATH=/Users/kaska/Documents/Projects/Major/arbiter/useragent
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_TARGET=/Users/kaska/Documents/Projects/Major/arbiter/useragent/lib/main.dart
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=1.0.0
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguOQ==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049NjczMjNkZTI4NQ==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NTg3YzE4Zjg3Mw==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC44
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=/Users/kaska/Documents/Projects/Major/arbiter/useragent/.dart_tool/package_config.json
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=/Users/kaska/.local/share/mise/installs/flutter/3.38.9-stable"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/kaska/Documents/Projects/Major/arbiter/useragent"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/kaska/Documents/Projects/Major/arbiter/useragent/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguOQ==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049NjczMjNkZTI4NQ==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NTg3YzE4Zjg3Mw==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC44"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/kaska/Documents/Projects/Major/arbiter/useragent/.dart_tool/package_config.json"
|
||||
@@ -1,4 +1,4 @@
|
||||
platform :osx, '10.15'
|
||||
platform :osx, '26.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@@ -42,6 +42,6 @@ SPEC CHECKSUMS:
|
||||
rive_native: 1c53d33e44c2b54424810effea4590671dd220c7
|
||||
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
|
||||
|
||||
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
|
||||
PODFILE CHECKSUM: 224cb1c0d6f5312abfc2477bcb5c7f1fca2574fb
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -195,7 +195,6 @@
|
||||
5385F9987FF8E7FD3BA4E87E /* Pods-RunnerTests.release.xcconfig */,
|
||||
ADF8B0EB51CA38AE67931C44 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -478,6 +477,7 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.useragent.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -493,6 +493,7 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.useragent.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -508,6 +509,7 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.useragent.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -560,6 +562,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
@@ -572,14 +575,22 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = 8L884L537J;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.markettakers.arbiter;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
@@ -588,6 +599,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Profile;
|
||||
@@ -643,6 +655,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
@@ -692,6 +705,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
@@ -704,14 +718,22 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = 8L884L537J;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.markettakers.arbiter;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
@@ -724,14 +746,22 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = 8L884L537J;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_INCOMING_NETWORK_CONNECTIONS = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.markettakers.arbiter;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
RUNTIME_EXCEPTION_ALLOW_JIT = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
@@ -740,6 +770,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -748,6 +779,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,5 +4,7 @@
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -282,7 +282,7 @@ packages:
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
@@ -770,7 +770,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "75ec242d22e950bdcc79ee38dd520ce4ee0bc491d7fadc4ea47694604d22bf06"
|
||||
|
||||
@@ -26,8 +26,10 @@ dependencies:
|
||||
cryptography_flutter: ^2.3.4
|
||||
riverpod_annotation: ^4.0.0
|
||||
grpc: ^5.1.0
|
||||
fixnum: ^1.1.1
|
||||
flutter_hooks: ^0.21.3+1
|
||||
auto_route: ^11.1.0
|
||||
protobuf: ^6.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user