neoagent 3.0.1-beta.9 → 3.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -10
- package/README.md +12 -3
- package/docs/automation.md +37 -0
- package/docs/benchmarking.md +102 -0
- package/docs/billing.md +34 -8
- package/docs/configuration.md +11 -2
- package/docs/getting-started.md +10 -8
- package/docs/operations.md +1 -1
- package/flutter_app/lib/main.dart +3 -0
- package/flutter_app/lib/main_account_settings.dart +138 -0
- package/flutter_app/lib/main_app_shell.dart +38 -13
- package/flutter_app/lib/main_billing.dart +1465 -0
- package/flutter_app/lib/main_chat.dart +1612 -214
- package/flutter_app/lib/main_controller.dart +398 -26
- package/flutter_app/lib/main_devices.dart +293 -207
- package/flutter_app/lib/main_integrations.dart +252 -0
- package/flutter_app/lib/main_models.dart +142 -0
- package/flutter_app/lib/main_navigation.dart +19 -1
- package/flutter_app/lib/main_operations.dart +638 -317
- package/flutter_app/lib/main_settings.dart +510 -300
- package/flutter_app/lib/main_shared.dart +2 -0
- package/flutter_app/lib/main_timeline.dart +1378 -0
- package/flutter_app/lib/src/backend_client.dart +149 -19
- package/flutter_app/lib/src/desktop_companion_actions.dart +132 -21
- package/flutter_app/lib/src/desktop_companion_io.dart +65 -1
- package/flutter_app/lib/src/desktop_companion_stub.dart +12 -0
- package/flutter_app/lib/src/desktop_ocr_bridge.dart +2 -0
- package/flutter_app/lib/src/desktop_ocr_bridge_io.dart +125 -0
- package/flutter_app/lib/src/desktop_ocr_bridge_stub.dart +30 -0
- package/flutter_app/lib/src/desktop_passive_history.dart +332 -0
- package/flutter_app/lib/src/recording_bridge_io.dart +80 -72
- package/flutter_app/lib/src/recording_bridge_web.dart +127 -114
- package/flutter_app/lib/src/recording_chunk_queue.dart +149 -0
- package/flutter_app/lib/src/recording_chunk_queue_io.dart +182 -0
- package/flutter_app/lib/src/recording_payloads.dart +9 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +25 -0
- package/flutter_app/windows/runner/flutter_window.cpp +75 -0
- package/landing/index.html +3 -3
- package/lib/manager.js +184 -66
- package/package.json +5 -1
- package/server/admin/access.js +12 -7
- package/server/admin/admin.js +436 -10
- package/server/admin/billing.js +158 -35
- package/server/admin/index.html +72 -2
- package/server/admin/users.js +15 -15
- package/server/db/database.js +125 -20
- package/server/guest-agent.browser.package.json +1 -0
- package/server/http/routes.js +1 -0
- package/server/http/static.js +4 -2
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/canvaskit/wimp.js.symbols +8475 -8467
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +91380 -87064
- package/server/routes/account.js +53 -0
- package/server/routes/admin.js +345 -64
- package/server/routes/agents.js +203 -21
- package/server/routes/billing.js +5 -0
- package/server/routes/browser.js +8 -1
- package/server/routes/recordings.js +96 -6
- package/server/routes/screenHistory.js +140 -2
- package/server/routes/timeline.js +43 -0
- package/server/services/account/erasure.js +263 -0
- package/server/services/ai/hooks.js +4 -1
- package/server/services/ai/loop/agent_engine_core.js +8 -1
- package/server/services/ai/loop/blank_recovery.js +36 -0
- package/server/services/ai/loop/conversation_loop.js +166 -33
- package/server/services/ai/messagingFallback.js +22 -0
- package/server/services/ai/rate_limits.js +28 -5
- package/server/services/ai/systemPrompt.js +6 -5
- package/server/services/ai/taskAnalysis.js +2 -0
- package/server/services/ai/toolEvidence.js +15 -0
- package/server/services/ai/toolResult.js +40 -0
- package/server/services/ai/tools.js +163 -4
- package/server/services/android/controller.js +6 -2
- package/server/services/billing/plans.js +2 -1
- package/server/services/browser/anti_detection.js +192 -0
- package/server/services/browser/controller.js +180 -54
- package/server/services/desktop/auth.js +3 -0
- package/server/services/desktop/registry.js +50 -2
- package/server/services/integrations/google/calendar.js +22 -14
- package/server/services/integrations/neomail/provider.js +993 -0
- package/server/services/integrations/registry.js +2 -0
- package/server/services/manager.js +12 -42
- package/server/services/memory/ingestion_chunking.js +268 -0
- package/server/services/messaging/telnyx.js +9 -8
- package/server/services/recordings/manager.js +60 -27
- package/server/services/runtime/docker-vm-manager.js +157 -266
- package/server/services/runtime/guest_bootstrap.js +17 -5
- package/server/services/runtime/guest_image.js +188 -0
- package/server/services/runtime/manager.js +0 -1
- package/server/services/runtime/validation.js +3 -8
- package/server/services/social_video/service.js +60 -10
- package/server/services/tasks/adapters/index.js +1 -1
- package/server/services/tasks/adapters/neomail_email_received.js +46 -0
- package/server/services/tasks/integration_runtime.js +67 -3
- package/server/services/tasks/runtime.js +240 -10
- package/server/services/tasks/task_repository.js +26 -6
- package/server/services/timeline/service.js +558 -0
- package/server/services/wearable/gateway.js +1 -1
- package/server/services/websocket.js +21 -3
- package/server/services/desktop/screenRecorder.js +0 -292
- package/server/services/desktop/screen_recorder_support.js +0 -46
package/.env.example
CHANGED
|
@@ -253,14 +253,6 @@ OLLAMA_URL=http://localhost:11434
|
|
|
253
253
|
# Connected integration memory refresh cadence in milliseconds.
|
|
254
254
|
NEOAGENT_MEMORY_INGESTION_INTERVAL_MS=600000
|
|
255
255
|
|
|
256
|
-
# Local screen OCR capture. This captures the backend host's macOS desktop,
|
|
257
|
-
# so enable it only when that host is the configured user's desktop.
|
|
258
|
-
NEOAGENT_SCREEN_RECORDER_ENABLED=false
|
|
259
|
-
# Existing NeoAgent user ID that owns all captured screen history. Required when enabled.
|
|
260
|
-
NEOAGENT_SCREEN_RECORDER_USER_ID=
|
|
261
|
-
NEOAGENT_SCREEN_RECORDER_INTERVAL_MS=10000
|
|
262
|
-
NEOAGENT_SCREEN_RECORDER_RETENTION_DAYS=7
|
|
263
|
-
|
|
264
256
|
########################################
|
|
265
257
|
# Messaging and voice integrations
|
|
266
258
|
########################################
|
|
@@ -278,8 +270,8 @@ TELNYX_WEBHOOK_TOKEN=
|
|
|
278
270
|
|
|
279
271
|
# Stripe API keys — required when billing is enabled.
|
|
280
272
|
# Use test keys (sk_test_...) during development; live keys in production.
|
|
281
|
-
# STRIPE_SECRET_KEY=
|
|
282
|
-
# STRIPE_PUBLISHABLE_KEY=
|
|
273
|
+
# STRIPE_SECRET_KEY=sk_test_...
|
|
274
|
+
# STRIPE_PUBLISHABLE_KEY=pk_test_...
|
|
283
275
|
|
|
284
276
|
# Webhook signing secret from your Stripe dashboard (Developers > Webhooks).
|
|
285
277
|
# STRIPE_WEBHOOK_SECRET=whsec_...
|
package/README.md
CHANGED
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
|
|
35
35
|
## 🚀 Install
|
|
36
36
|
|
|
37
|
-
NeoAgent supports macOS and Linux hosts. You need Node.js 20 or newer
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
NeoAgent supports macOS and Linux hosts. You need Node.js 20 or newer and
|
|
38
|
+
Docker for the isolated browser and terminal runtime; the installer handles the
|
|
39
|
+
rest of the application setup and pre-builds the guest runtime image when Docker
|
|
40
|
+
is available.
|
|
40
41
|
|
|
41
42
|
```bash
|
|
42
43
|
npm install -g neoagent
|
|
@@ -90,8 +91,16 @@ changes and rough edges. Review the
|
|
|
90
91
|
[security boundaries](docs/security-boundaries.md) before connecting sensitive
|
|
91
92
|
accounts or giving the agent access to a personal workstation.
|
|
92
93
|
|
|
94
|
+
## 📊 Benchmarks
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
See [Benchmarking](docs/benchmarking.md) for the reproducibility details and suite coverage.
|
|
98
|
+
|
|
93
99
|
Start with the [documentation](https://neolabs-systems.github.io/NeoAgent/docs/).
|
|
94
100
|
Use [GitHub Discussions](https://github.com/NeoLabs-Systems/NeoAgent/discussions)
|
|
95
101
|
for questions and [GitHub Issues](https://github.com/NeoLabs-Systems/NeoAgent/issues)
|
|
96
102
|
for reproducible bugs. Security reports belong in the process described by
|
|
97
103
|
[SECURITY.md](SECURITY.md).
|
|
104
|
+
|
|
105
|
+
*Made with ❤️ by [Neo](https://github.com/neooriginal) · [NeoLabs Systems](https://github.com/NeoLabs-Systems)*
|
|
106
|
+
|
package/docs/automation.md
CHANGED
|
@@ -62,12 +62,49 @@ when no reply is needed.
|
|
|
62
62
|
Prefer official integrations or MCP tools over browser automation when both
|
|
63
63
|
can perform the action. Structured tools are easier to restrict and diagnose.
|
|
64
64
|
|
|
65
|
+
## Bot-protected pages
|
|
66
|
+
|
|
67
|
+
The isolated VM browser uses native anti-detection hardening inspired by
|
|
68
|
+
Botasaurus' public anti-detect browser patterns: stable browser profiles,
|
|
69
|
+
common desktop viewport sizes, less mechanical pointer movement, referrer-aware
|
|
70
|
+
navigation, and challenge detection telemetry. NeoAgent does not embed or
|
|
71
|
+
depend on Botasaurus for this path.
|
|
72
|
+
|
|
73
|
+
`browser_navigate` accepts optional `referrerMode` values of `direct`,
|
|
74
|
+
`google`, or `current`. By default it also retries once with a Google referrer
|
|
75
|
+
when a known bot challenge is detected. Browser results can include
|
|
76
|
+
`botDetection` so runs can report that a page is blocked instead of treating the
|
|
77
|
+
challenge page as ordinary content.
|
|
78
|
+
|
|
79
|
+
Browser automation still must comply with the target site's terms and applicable
|
|
80
|
+
law. URL safety checks and the VM device-access restrictions remain in effect.
|
|
81
|
+
|
|
65
82
|
## Runs and delivery
|
|
66
83
|
|
|
67
84
|
Open **Runs** to inspect the trigger, tool calls, approvals, output, and error
|
|
68
85
|
for each execution. Delivery requires a configured messaging destination; a
|
|
69
86
|
completed run can still have a delivery error.
|
|
70
87
|
|
|
88
|
+
## Loop budgets
|
|
89
|
+
|
|
90
|
+
Scheduled and event-triggered tasks run autonomously under a per-task loop
|
|
91
|
+
budget. By default, a task can run 24 times or spend 250k model tokens per day.
|
|
92
|
+
At the cap, or when paused, the task is skipped before a model call.
|
|
93
|
+
|
|
94
|
+
Advanced task configs can override the defaults:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"loopBudget": {
|
|
99
|
+
"maxRunsPerDay": 12,
|
|
100
|
+
"maxTokensPerDay": 150000,
|
|
101
|
+
"paused": false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Set `loopBudget.paused` or `loopPaused` to `true` as a per-task kill switch.
|
|
107
|
+
|
|
71
108
|
## Safety
|
|
72
109
|
|
|
73
110
|
- Start with read-only integration accounts.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Benchmarking
|
|
2
|
+
|
|
3
|
+
NeoAgent includes an end-to-end benchmark harness that drives the app through
|
|
4
|
+
its authenticated HTTP surface instead of calling internal engine methods
|
|
5
|
+
directly. The harness configures OpenRouter models, submits benchmark tasks,
|
|
6
|
+
collects run events and usage data, and writes normalized result artifacts.
|
|
7
|
+
|
|
8
|
+
## Latest dashboard
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
The generated image is backed by these local outputs:
|
|
13
|
+
|
|
14
|
+
- `benchmark/results/latest-results.json`
|
|
15
|
+
- `benchmark/results/latest-summary.json`
|
|
16
|
+
- `benchmark/results/latest-summary.md`
|
|
17
|
+
- `static/benchmarks/latest-dashboard.png`
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run benchmark:setup
|
|
23
|
+
npm run benchmark:run
|
|
24
|
+
npm run benchmark:report
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Model selection
|
|
28
|
+
|
|
29
|
+
The benchmark harness is OpenRouter-first. Edit the `OPENROUTER_MODEL_IDS`
|
|
30
|
+
variable in `benchmark/config.js` to pin exact model IDs, or leave it empty to
|
|
31
|
+
use the current cheap-tier OpenRouter models discovered at runtime.
|
|
32
|
+
|
|
33
|
+
## Benchmark coverage
|
|
34
|
+
|
|
35
|
+
- `GAIA`, `BrowseComp`, `WebArena`, `VisualWebArena`, and `SWE-bench` are wired
|
|
36
|
+
as exact public-suite adapters. When their official datasets or external
|
|
37
|
+
runners are not installed, they are reported as blocked instead of being
|
|
38
|
+
approximated.
|
|
39
|
+
- `NeoAgent Representative Tasks` runs first-party agent tasks through
|
|
40
|
+
`/api/agents` and scores them from the persisted run details.
|
|
41
|
+
- `NeoAgent Memory Retrieval` seeds memories over `/api/memory`, recalls them
|
|
42
|
+
through the app routes, and scores retrieval quality with the same metric
|
|
43
|
+
code used by the server evaluation module.
|
|
44
|
+
|
|
45
|
+
## Reproducibility
|
|
46
|
+
|
|
47
|
+
- Public-suite setup manifests are written under `benchmark/workdir/<suite>/`.
|
|
48
|
+
- The harness uses one benchmark account and persists per-run usage summaries
|
|
49
|
+
through the existing NeoAgent run detail endpoints.
|
|
50
|
+
- Blocked suites are intentional and explicit. The harness never substitutes a
|
|
51
|
+
simplified local task and labels it as a public benchmark result.
|
|
52
|
+
|
|
53
|
+
## Public benchmark prerequisites
|
|
54
|
+
|
|
55
|
+
### GAIA
|
|
56
|
+
|
|
57
|
+
- Access to the gated [GAIA dataset](https://huggingface.co/datasets/gaia-benchmark/GAIA).
|
|
58
|
+
- `HF_TOKEN` in the environment.
|
|
59
|
+
- A Python environment with `datasets` installed if you want `benchmark:setup`
|
|
60
|
+
to export a normalized local cache automatically.
|
|
61
|
+
- Either an exact evaluator wired through
|
|
62
|
+
`NEOAGENT_BENCHMARK_GAIA_RUNNER` or a repository-local exact runner added to
|
|
63
|
+
the harness. The current adapter intentionally reports `blocked` until an
|
|
64
|
+
official evaluator path is configured.
|
|
65
|
+
|
|
66
|
+
### BrowseComp
|
|
67
|
+
|
|
68
|
+
- A checkout of [openai/simple-evals](https://github.com/openai/simple-evals),
|
|
69
|
+
which `benchmark:setup` can clone into `benchmark/workdir/browsecomp/`.
|
|
70
|
+
- Exact BrowseComp case extraction from that repo.
|
|
71
|
+
- An official evaluator command exposed through
|
|
72
|
+
`NEOAGENT_BENCHMARK_BROWSECOMP_RUNNER`.
|
|
73
|
+
|
|
74
|
+
### WebArena
|
|
75
|
+
|
|
76
|
+
- Docker installed and running.
|
|
77
|
+
- The official [WebArena](https://webarena.dev/) environment and task sites.
|
|
78
|
+
- Browser automation dependencies required by the official runner.
|
|
79
|
+
- An exact runner command exposed through
|
|
80
|
+
`NEOAGENT_BENCHMARK_WEBARENA_RUNNER`.
|
|
81
|
+
|
|
82
|
+
### VisualWebArena
|
|
83
|
+
|
|
84
|
+
- Docker installed and running.
|
|
85
|
+
- The official VisualWebArena environment.
|
|
86
|
+
- A vision-capable configured benchmark model.
|
|
87
|
+
- An exact runner command exposed through
|
|
88
|
+
`NEOAGENT_BENCHMARK_VISUAL_WEBARENA_RUNNER`.
|
|
89
|
+
|
|
90
|
+
### SWE-bench
|
|
91
|
+
|
|
92
|
+
- Docker installed and running.
|
|
93
|
+
- A checkout of [SWE-bench](https://github.com/SWE-bench/SWE-bench), which
|
|
94
|
+
`benchmark:setup` can clone into `benchmark/workdir/swebench/`.
|
|
95
|
+
- The official dataset/runtime and enough disk/network budget for repo/image
|
|
96
|
+
setup.
|
|
97
|
+
- An exact runner command exposed through
|
|
98
|
+
`NEOAGENT_BENCHMARK_SWEBENCH_RUNNER`.
|
|
99
|
+
|
|
100
|
+
On this machine, `docker` is currently missing, so exact `WebArena`,
|
|
101
|
+
`VisualWebArena`, and `SWE-bench` execution remain blocked until Docker and the
|
|
102
|
+
official runners are installed.
|
package/docs/billing.md
CHANGED
|
@@ -7,19 +7,40 @@ anywhere in the application.
|
|
|
7
7
|
|
|
8
8
|
## Enable billing
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Run the interactive setup wizard:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
neoagent
|
|
14
|
-
neoagent env set STRIPE_SECRET_KEY sk_live_...
|
|
15
|
-
neoagent env set STRIPE_PUBLISHABLE_KEY pk_live_...
|
|
16
|
-
neoagent env set STRIPE_WEBHOOK_SECRET whsec_...
|
|
13
|
+
neoagent billing setup
|
|
17
14
|
```
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
The wizard prompts for your Stripe API keys, webhook secret, and trial length,
|
|
17
|
+
then asks whether to enable billing immediately. Use Stripe test keys
|
|
18
|
+
(`sk_test_...`, `pk_test_...`) during development.
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
To check the current configuration at any time:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
neoagent billing # or: neoagent billing status
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To toggle billing without re-running setup:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
neoagent billing enable # set NEOAGENT_BILLING_ENABLED=true and restart
|
|
30
|
+
neoagent billing disable # set NEOAGENT_BILLING_ENABLED=false and restart
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
When enabled the admin dashboard will show a **Billing** navigation item and
|
|
34
|
+
the `/api/billing/*` endpoints become active.
|
|
35
|
+
|
|
36
|
+
> **Manual alternative** — you can also set variables directly and restart:
|
|
37
|
+
> ```bash
|
|
38
|
+
> neoagent env set STRIPE_SECRET_KEY sk_live_...
|
|
39
|
+
> neoagent env set STRIPE_PUBLISHABLE_KEY pk_live_...
|
|
40
|
+
> neoagent env set STRIPE_WEBHOOK_SECRET whsec_...
|
|
41
|
+
> neoagent env set NEOAGENT_BILLING_ENABLED true
|
|
42
|
+
> neoagent restart
|
|
43
|
+
> ```
|
|
23
44
|
|
|
24
45
|
## Subscription plans
|
|
25
46
|
|
|
@@ -40,6 +61,11 @@ Token limits are written directly to the user account when a subscription
|
|
|
40
61
|
becomes active or is updated via webhook. The existing rate-limit enforcement
|
|
41
62
|
in the runtime reads them with no additional configuration.
|
|
42
63
|
|
|
64
|
+
Token limits gate interactive run starts. Background task executions are not
|
|
65
|
+
blocked by the internal token admission check, so scheduled and integration
|
|
66
|
+
tasks can still run while a user is over quota. Their token usage is still
|
|
67
|
+
recorded and included in account and admin usage snapshots.
|
|
68
|
+
|
|
43
69
|
### Free plan
|
|
44
70
|
|
|
45
71
|
Create a plan with **Price = 0** to serve as the default for new users. If a
|
package/docs/configuration.md
CHANGED
|
@@ -99,8 +99,17 @@ reset, email changes, and security notifications.
|
|
|
99
99
|
|
|
100
100
|
## Billing
|
|
101
101
|
|
|
102
|
-
Billing is disabled by default.
|
|
103
|
-
|
|
102
|
+
Billing is disabled by default. Use the interactive wizard to configure it:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
neoagent billing setup # guided Stripe key and webhook setup
|
|
106
|
+
neoagent billing # show current status
|
|
107
|
+
neoagent billing enable # activate billing and restart
|
|
108
|
+
neoagent billing disable # deactivate billing and restart
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
See [Billing](billing.md) for the full setup guide, webhook configuration, and
|
|
112
|
+
plan management.
|
|
104
113
|
|
|
105
114
|
| Variable | Default | Purpose |
|
|
106
115
|
|---|---|---|
|
package/docs/getting-started.md
CHANGED
|
@@ -9,18 +9,20 @@ remote access to work.
|
|
|
9
9
|
- Node.js 20 or newer with npm
|
|
10
10
|
- A macOS or Linux user account that can install packages
|
|
11
11
|
- Enough disk and memory for the models and runtime you choose
|
|
12
|
-
-
|
|
12
|
+
- Docker for the isolated browser and terminal runtime
|
|
13
13
|
|
|
14
|
-
The
|
|
15
|
-
|
|
14
|
+
The isolated browser and CLI run each user's guest agent in a Docker container.
|
|
15
|
+
Install Docker first; the NeoAgent installer detects it and pre-builds the guest
|
|
16
|
+
runtime image (browser and dependencies baked in) so the first browser or CLI
|
|
17
|
+
action is instant. If Docker is missing, the rest of NeoAgent still installs and
|
|
18
|
+
the cloud browser/CLI become available once Docker is running.
|
|
16
19
|
|
|
17
20
|
```bash
|
|
18
|
-
# macOS
|
|
19
|
-
|
|
21
|
+
# macOS — Docker Desktop
|
|
22
|
+
# https://www.docker.com/products/docker-desktop/
|
|
20
23
|
|
|
21
|
-
# Ubuntu or Debian
|
|
22
|
-
|
|
23
|
-
sudo apt-get install -y qemu-system qemu-utils
|
|
24
|
+
# Ubuntu or Debian — Docker Engine
|
|
25
|
+
# https://docs.docker.com/engine/install/
|
|
24
26
|
```
|
|
25
27
|
|
|
26
28
|
## Install NeoAgent
|
package/docs/operations.md
CHANGED
|
@@ -66,7 +66,7 @@ Protect backups as credentials and personal data.
|
|
|
66
66
|
|---|---|
|
|
67
67
|
| Service does not start | `neoagent status`, then `neoagent logs` |
|
|
68
68
|
| Chat has no response | Provider connection, selected model, provider quota |
|
|
69
|
-
| Browser or shell unavailable |
|
|
69
|
+
| Browser or shell unavailable | Docker installed and running, then first runtime boot |
|
|
70
70
|
| OAuth fails | Reachable HTTPS `PUBLIC_URL` and exact callback URI |
|
|
71
71
|
| Messaging receives nothing | Channel credentials, webhook, and allowlists |
|
|
72
72
|
| Task does not run | Enabled state, trigger account, next run, **Runs** |
|
|
@@ -20,6 +20,7 @@ import 'package:image/image.dart' as img;
|
|
|
20
20
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
|
21
21
|
import 'package:qr_flutter/qr_flutter.dart';
|
|
22
22
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
23
|
+
import 'package:url_launcher/url_launcher.dart' as url_launcher;
|
|
23
24
|
import 'package:socket_io_client/socket_io_client.dart' as io;
|
|
24
25
|
import 'package:audioplayers/audioplayers.dart';
|
|
25
26
|
import 'package:tray_manager/tray_manager.dart';
|
|
@@ -60,6 +61,7 @@ part 'main_models.dart';
|
|
|
60
61
|
part 'main_shared.dart';
|
|
61
62
|
part 'main_voice_assistant.dart';
|
|
62
63
|
part 'main_navigation.dart';
|
|
64
|
+
part 'main_timeline.dart';
|
|
63
65
|
part 'main_runtime.dart';
|
|
64
66
|
part 'main_controller.dart';
|
|
65
67
|
part 'main_devices.dart';
|
|
@@ -71,6 +73,7 @@ part 'main_security.dart';
|
|
|
71
73
|
part 'main_model_picker.dart';
|
|
72
74
|
part 'main_operations.dart';
|
|
73
75
|
part 'main_admin.dart';
|
|
76
|
+
part 'main_billing.dart';
|
|
74
77
|
part 'main_unified.dart';
|
|
75
78
|
part 'main_install.dart';
|
|
76
79
|
|
|
@@ -161,6 +161,8 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
161
161
|
String? _emailInlineError;
|
|
162
162
|
String? _passwordSuccessMessage;
|
|
163
163
|
String? _passwordInlineError;
|
|
164
|
+
bool _isExportingData = false;
|
|
165
|
+
bool _isDeletingAccount = false;
|
|
164
166
|
|
|
165
167
|
@override
|
|
166
168
|
void initState() {
|
|
@@ -592,10 +594,146 @@ class _AccountSettingsPanelState extends State<AccountSettingsPanel> {
|
|
|
592
594
|
.toList(),
|
|
593
595
|
),
|
|
594
596
|
],
|
|
597
|
+
const SizedBox(height: 28),
|
|
598
|
+
const _SectionTitle('Your data'),
|
|
599
|
+
const SizedBox(height: 10),
|
|
600
|
+
Text(
|
|
601
|
+
'Download a copy of your data, or permanently delete your account. '
|
|
602
|
+
'Deletion removes all your conversations, memories, files, tasks and '
|
|
603
|
+
'settings and cannot be undone.',
|
|
604
|
+
style: TextStyle(color: _textSecondary, height: 1.45),
|
|
605
|
+
),
|
|
606
|
+
const SizedBox(height: 14),
|
|
607
|
+
Wrap(
|
|
608
|
+
spacing: 12,
|
|
609
|
+
runSpacing: 12,
|
|
610
|
+
children: <Widget>[
|
|
611
|
+
OutlinedButton.icon(
|
|
612
|
+
onPressed: _isExportingData ? null : _exportMyData,
|
|
613
|
+
icon: _isExportingData
|
|
614
|
+
? const SizedBox.square(
|
|
615
|
+
dimension: 16,
|
|
616
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
617
|
+
)
|
|
618
|
+
: const Icon(Icons.download_outlined),
|
|
619
|
+
label: const Text('Export my data'),
|
|
620
|
+
),
|
|
621
|
+
OutlinedButton.icon(
|
|
622
|
+
onPressed: _isDeletingAccount ? null : _confirmDeleteAccount,
|
|
623
|
+
style: OutlinedButton.styleFrom(
|
|
624
|
+
foregroundColor: _danger,
|
|
625
|
+
side: BorderSide(color: _danger.withValues(alpha: 0.6)),
|
|
626
|
+
),
|
|
627
|
+
icon: _isDeletingAccount
|
|
628
|
+
? const SizedBox.square(
|
|
629
|
+
dimension: 16,
|
|
630
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
631
|
+
)
|
|
632
|
+
: const Icon(Icons.delete_forever_outlined),
|
|
633
|
+
label: const Text('Delete account'),
|
|
634
|
+
),
|
|
635
|
+
],
|
|
636
|
+
),
|
|
595
637
|
],
|
|
596
638
|
);
|
|
597
639
|
}
|
|
598
640
|
|
|
641
|
+
Future<void> _exportMyData() async {
|
|
642
|
+
setState(() => _isExportingData = true);
|
|
643
|
+
try {
|
|
644
|
+
final export = await widget.controller.backendClient.exportMyData(
|
|
645
|
+
widget.controller.backendUrl,
|
|
646
|
+
);
|
|
647
|
+
final pretty = const JsonEncoder.withIndent(' ').convert(export);
|
|
648
|
+
await Clipboard.setData(ClipboardData(text: pretty));
|
|
649
|
+
if (!mounted) return;
|
|
650
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
651
|
+
const SnackBar(
|
|
652
|
+
content: Text('Your data export was copied to the clipboard.'),
|
|
653
|
+
),
|
|
654
|
+
);
|
|
655
|
+
} catch (err) {
|
|
656
|
+
if (!mounted) return;
|
|
657
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
658
|
+
SnackBar(content: Text('Could not export your data: $err')),
|
|
659
|
+
);
|
|
660
|
+
} finally {
|
|
661
|
+
if (mounted) setState(() => _isExportingData = false);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
Future<void> _confirmDeleteAccount() async {
|
|
666
|
+
final username = widget.controller.user?['username']?.toString() ?? '';
|
|
667
|
+
final confirmController = TextEditingController();
|
|
668
|
+
final confirmed = await showDialog<bool>(
|
|
669
|
+
context: context,
|
|
670
|
+
builder: (dialogContext) {
|
|
671
|
+
return StatefulBuilder(
|
|
672
|
+
builder: (context, setDialogState) {
|
|
673
|
+
final matches = confirmController.text.trim() == username;
|
|
674
|
+
return AlertDialog(
|
|
675
|
+
title: const Text('Delete account permanently?'),
|
|
676
|
+
content: Column(
|
|
677
|
+
mainAxisSize: MainAxisSize.min,
|
|
678
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
679
|
+
children: <Widget>[
|
|
680
|
+
const Text(
|
|
681
|
+
'This permanently erases all of your data and cannot be '
|
|
682
|
+
'undone. Type your username to confirm.',
|
|
683
|
+
),
|
|
684
|
+
const SizedBox(height: 14),
|
|
685
|
+
TextField(
|
|
686
|
+
controller: confirmController,
|
|
687
|
+
autofocus: true,
|
|
688
|
+
onChanged: (_) => setDialogState(() {}),
|
|
689
|
+
decoration: InputDecoration(
|
|
690
|
+
labelText: 'Username',
|
|
691
|
+
hintText: username,
|
|
692
|
+
),
|
|
693
|
+
),
|
|
694
|
+
],
|
|
695
|
+
),
|
|
696
|
+
actions: <Widget>[
|
|
697
|
+
TextButton(
|
|
698
|
+
onPressed: () => Navigator.of(dialogContext).pop(false),
|
|
699
|
+
child: const Text('Cancel'),
|
|
700
|
+
),
|
|
701
|
+
FilledButton(
|
|
702
|
+
style: FilledButton.styleFrom(backgroundColor: _danger),
|
|
703
|
+
onPressed: matches
|
|
704
|
+
? () => Navigator.of(dialogContext).pop(true)
|
|
705
|
+
: null,
|
|
706
|
+
child: const Text('Delete forever'),
|
|
707
|
+
),
|
|
708
|
+
],
|
|
709
|
+
);
|
|
710
|
+
},
|
|
711
|
+
);
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
confirmController.dispose();
|
|
715
|
+
if (confirmed != true) return;
|
|
716
|
+
|
|
717
|
+
setState(() => _isDeletingAccount = true);
|
|
718
|
+
try {
|
|
719
|
+
await widget.controller.backendClient.deleteMyAccount(
|
|
720
|
+
baseUrl: widget.controller.backendUrl,
|
|
721
|
+
confirmUsername: username,
|
|
722
|
+
);
|
|
723
|
+
if (!mounted) return;
|
|
724
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
725
|
+
const SnackBar(content: Text('Your account has been deleted.')),
|
|
726
|
+
);
|
|
727
|
+
await widget.controller.logout();
|
|
728
|
+
} catch (err) {
|
|
729
|
+
if (!mounted) return;
|
|
730
|
+
setState(() => _isDeletingAccount = false);
|
|
731
|
+
ScaffoldMessenger.of(context).showSnackBar(
|
|
732
|
+
SnackBar(content: Text('Could not delete your account: $err')),
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
599
737
|
String _formatTokens(int amount) {
|
|
600
738
|
if (amount >= 1000000) {
|
|
601
739
|
final value = amount / 1000000;
|
|
@@ -205,9 +205,15 @@ class _BackendSetupViewState extends State<BackendSetupView> {
|
|
|
205
205
|
const SizedBox(height: 10),
|
|
206
206
|
Center(
|
|
207
207
|
child: TextButton.icon(
|
|
208
|
-
onPressed: () =>
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
onPressed: () =>
|
|
209
|
+
setState(() => _localInstall = true),
|
|
210
|
+
icon: const Icon(
|
|
211
|
+
Icons.download_outlined,
|
|
212
|
+
size: 16,
|
|
213
|
+
),
|
|
214
|
+
label: const Text(
|
|
215
|
+
'Install NeoAgent on this machine',
|
|
216
|
+
),
|
|
211
217
|
),
|
|
212
218
|
),
|
|
213
219
|
],
|
|
@@ -268,7 +274,9 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
268
274
|
if (!mounted) return;
|
|
269
275
|
setState(() => _phase = _LocalInstallPhase.checking);
|
|
270
276
|
|
|
271
|
-
final r = await Process.run(Platform.isWindows ? 'where' : 'which', [
|
|
277
|
+
final r = await Process.run(Platform.isWindows ? 'where' : 'which', [
|
|
278
|
+
'node',
|
|
279
|
+
]);
|
|
272
280
|
if (r.exitCode == 0) {
|
|
273
281
|
_nodePath = (r.stdout as String).trim().split('\n').first.trim();
|
|
274
282
|
} else if (!Platform.isWindows) {
|
|
@@ -315,7 +323,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
315
323
|
if (!_ensureEnvFile()) {
|
|
316
324
|
setState(() {
|
|
317
325
|
_phase = _LocalInstallPhase.failed;
|
|
318
|
-
_errorMsg =
|
|
326
|
+
_errorMsg =
|
|
327
|
+
'Could not create ~/.neoagent/runtime/.env — check directory permissions.';
|
|
319
328
|
});
|
|
320
329
|
return;
|
|
321
330
|
}
|
|
@@ -324,11 +333,10 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
324
333
|
_log.clear();
|
|
325
334
|
});
|
|
326
335
|
|
|
327
|
-
final process = await Process.start(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
);
|
|
336
|
+
final process = await Process.start(_nodePath!, [
|
|
337
|
+
'bin/neoagent.js',
|
|
338
|
+
'install',
|
|
339
|
+
], workingDirectory: _installDir);
|
|
332
340
|
_proc = process;
|
|
333
341
|
|
|
334
342
|
void onChunk(String data) {
|
|
@@ -349,7 +357,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
349
357
|
if (!mounted) return;
|
|
350
358
|
setState(() {
|
|
351
359
|
_phase = exit == 0 ? _LocalInstallPhase.done : _LocalInstallPhase.failed;
|
|
352
|
-
if (exit != 0)
|
|
360
|
+
if (exit != 0)
|
|
361
|
+
_errorMsg = 'Installation failed (exit $exit). Check the log above.';
|
|
353
362
|
});
|
|
354
363
|
}
|
|
355
364
|
|
|
@@ -388,7 +397,10 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
388
397
|
const SizedBox(height: 16),
|
|
389
398
|
const _BrandLockup(logoSize: 60),
|
|
390
399
|
const SizedBox(height: 22),
|
|
391
|
-
Text(
|
|
400
|
+
Text(
|
|
401
|
+
'INSTALL LOCALLY',
|
|
402
|
+
style: _sectionEyebrowStyle(),
|
|
403
|
+
),
|
|
392
404
|
const SizedBox(height: 10),
|
|
393
405
|
Text(
|
|
394
406
|
'Install NeoAgent as a background service',
|
|
@@ -459,7 +471,8 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
459
471
|
final proc = _proc;
|
|
460
472
|
proc?.kill(ProcessSignal.sigterm);
|
|
461
473
|
await proc?.exitCode;
|
|
462
|
-
if (mounted)
|
|
474
|
+
if (mounted)
|
|
475
|
+
setState(() => _phase = _LocalInstallPhase.ready);
|
|
463
476
|
},
|
|
464
477
|
child: const Text('Cancel'),
|
|
465
478
|
),
|
|
@@ -1695,6 +1708,7 @@ class _HomeViewState extends State<HomeView> {
|
|
|
1695
1708
|
.allowMessagingSuggestion(
|
|
1696
1709
|
notice.platform,
|
|
1697
1710
|
suggestion,
|
|
1711
|
+
chatId: notice.chatId,
|
|
1698
1712
|
);
|
|
1699
1713
|
},
|
|
1700
1714
|
icon: Icon(Icons.verified_user_outlined),
|
|
@@ -1716,6 +1730,13 @@ class _HomeViewState extends State<HomeView> {
|
|
|
1716
1730
|
},
|
|
1717
1731
|
child: Text('Open Messaging'),
|
|
1718
1732
|
),
|
|
1733
|
+
TextButton(
|
|
1734
|
+
onPressed: () async {
|
|
1735
|
+
Navigator.of(dialogContext).pop();
|
|
1736
|
+
await widget.controller.ignoreBlockedSender(notice);
|
|
1737
|
+
},
|
|
1738
|
+
child: Text('Ignore'),
|
|
1739
|
+
),
|
|
1719
1740
|
TextButton(
|
|
1720
1741
|
onPressed: () => Navigator.of(dialogContext).pop(),
|
|
1721
1742
|
child: Text('Dismiss'),
|
|
@@ -2488,6 +2509,8 @@ class _SectionBody extends StatelessWidget {
|
|
|
2488
2509
|
switch (controller.selectedSection) {
|
|
2489
2510
|
case AppSection.chat:
|
|
2490
2511
|
return ChatPanel(controller: controller);
|
|
2512
|
+
case AppSection.timeline:
|
|
2513
|
+
return TimelinePanel(controller: controller);
|
|
2491
2514
|
case AppSection.voiceAssistant:
|
|
2492
2515
|
return VoiceAssistantPanel(controller: controller);
|
|
2493
2516
|
case AppSection.devices:
|
|
@@ -2522,6 +2545,8 @@ class _SectionBody extends StatelessWidget {
|
|
|
2522
2545
|
: ChatPanel(controller: controller);
|
|
2523
2546
|
case AppSection.server:
|
|
2524
2547
|
return ServerPanel(controller: controller);
|
|
2548
|
+
case AppSection.billing:
|
|
2549
|
+
return BillingPanel(controller: controller);
|
|
2525
2550
|
}
|
|
2526
2551
|
}
|
|
2527
2552
|
}
|