neoagent 3.3.0 → 3.3.1-beta.1
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/LICENSE +67 -80
- package/README.md +11 -1
- package/docs/licensing.md +44 -0
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_app_shell.dart +8 -6
- package/flutter_app/lib/main_controller.dart +7 -1
- package/flutter_app/lib/main_install.dart +9 -7
- package/flutter_app/lib/src/local_runtime_paths.dart +60 -0
- package/lib/manager.js +10 -2
- package/package.json +1 -1
- package/runtime/paths.js +70 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +56150 -56122
package/LICENSE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
GNU GENERAL PUBLIC LICENSE
|
|
2
|
-
Version 3,
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
3
|
|
|
4
4
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
5
5
|
Everyone is permitted to copy and distribute verbatim copies
|
|
@@ -7,17 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
Preamble
|
|
9
9
|
|
|
10
|
-
The GNU General Public License is a free, copyleft license for
|
|
11
|
-
software and other kinds of works
|
|
10
|
+
The GNU Affero General Public License is a free, copyleft license for
|
|
11
|
+
software and other kinds of works, specifically designed to ensure
|
|
12
|
+
cooperation with the community in the case of network server software.
|
|
12
13
|
|
|
13
14
|
The licenses for most software and other practical works are designed
|
|
14
15
|
to take away your freedom to share and change the works. By contrast,
|
|
15
|
-
|
|
16
|
+
our General Public Licenses are intended to guarantee your freedom to
|
|
16
17
|
share and change all versions of a program--to make sure it remains free
|
|
17
|
-
software for all its users.
|
|
18
|
-
GNU General Public License for most of our software; it applies also to
|
|
19
|
-
any other work released this way by its authors. You can apply it to
|
|
20
|
-
your programs, too.
|
|
18
|
+
software for all its users.
|
|
21
19
|
|
|
22
20
|
When we speak of free software, we are referring to freedom, not
|
|
23
21
|
price. Our General Public Licenses are designed to make sure that you
|
|
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
|
|
26
24
|
want it, that you can change the software or use pieces of it in new
|
|
27
25
|
free programs, and that you know you can do these things.
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
that
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
products. If such problems arise substantially in other domains, we
|
|
58
|
-
stand ready to extend this provision to those domains in future versions
|
|
59
|
-
of the GPL, as needed to protect the freedom of users.
|
|
60
|
-
|
|
61
|
-
Finally, every program is threatened constantly by software patents.
|
|
62
|
-
States should not allow patents to restrict development and use of
|
|
63
|
-
software on general-purpose computers, but in those that do, we wish to
|
|
64
|
-
avoid the special danger that patents applied to a free program could
|
|
65
|
-
make it effectively proprietary. To prevent this, the GPL assures that
|
|
66
|
-
patents cannot be used to render the program non-free.
|
|
27
|
+
Developers that use our General Public Licenses protect your rights
|
|
28
|
+
with two steps: (1) assert copyright on the software, and (2) offer
|
|
29
|
+
you this License which gives you legal permission to copy, distribute
|
|
30
|
+
and/or modify the software.
|
|
31
|
+
|
|
32
|
+
A secondary benefit of defending all users' freedom is that
|
|
33
|
+
improvements made in alternate versions of the program, if they
|
|
34
|
+
receive widespread use, become available for other developers to
|
|
35
|
+
incorporate. Many developers of free software are heartened and
|
|
36
|
+
encouraged by the resulting cooperation. However, in the case of
|
|
37
|
+
software used on network servers, this result may fail to come about.
|
|
38
|
+
The GNU General Public License permits making a modified version and
|
|
39
|
+
letting the public access it on a server without ever releasing its
|
|
40
|
+
source code to the public.
|
|
41
|
+
|
|
42
|
+
The GNU Affero General Public License is designed specifically to
|
|
43
|
+
ensure that, in such cases, the modified source code becomes available
|
|
44
|
+
to the community. It requires the operator of a network server to
|
|
45
|
+
provide the source code of the modified version running there to the
|
|
46
|
+
users of that server. Therefore, public use of a modified version, on
|
|
47
|
+
a publicly accessible server, gives the public access to the source
|
|
48
|
+
code of the modified version.
|
|
49
|
+
|
|
50
|
+
An older license, called the Affero General Public License and
|
|
51
|
+
published by Affero, was designed to accomplish similar goals. This is
|
|
52
|
+
a different license, not a version of the Affero GPL, but Affero has
|
|
53
|
+
released a new version of the Affero GPL which permits relicensing under
|
|
54
|
+
this license.
|
|
67
55
|
|
|
68
56
|
The precise terms and conditions for copying, distribution and
|
|
69
57
|
modification follow.
|
|
@@ -72,7 +60,7 @@ modification follow.
|
|
|
72
60
|
|
|
73
61
|
0. Definitions.
|
|
74
62
|
|
|
75
|
-
"This License" refers to version 3 of the GNU General Public License.
|
|
63
|
+
"This License" refers to version 3 of the GNU Affero General Public License.
|
|
76
64
|
|
|
77
65
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
78
66
|
works, such as semiconductor masks.
|
|
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
|
|
549
537
|
the Program, the only way you could satisfy both those terms and this
|
|
550
538
|
License would be to refrain entirely from conveying the Program.
|
|
551
539
|
|
|
552
|
-
13. Use with the GNU
|
|
540
|
+
13. Remote Network Interaction; Use with the GNU General Public License.
|
|
541
|
+
|
|
542
|
+
Notwithstanding any other provision of this License, if you modify the
|
|
543
|
+
Program, your modified version must prominently offer all users
|
|
544
|
+
interacting with it remotely through a computer network (if your version
|
|
545
|
+
supports such interaction) an opportunity to receive the Corresponding
|
|
546
|
+
Source of your version by providing access to the Corresponding Source
|
|
547
|
+
from a network server at no charge, through some standard or customary
|
|
548
|
+
means of facilitating copying of software. This Corresponding Source
|
|
549
|
+
shall include the Corresponding Source for any work covered by version 3
|
|
550
|
+
of the GNU General Public License that is incorporated pursuant to the
|
|
551
|
+
following paragraph.
|
|
553
552
|
|
|
554
553
|
Notwithstanding any other provision of this License, you have
|
|
555
554
|
permission to link or combine any covered work with a work licensed
|
|
556
|
-
under version 3 of the GNU
|
|
555
|
+
under version 3 of the GNU General Public License into a single
|
|
557
556
|
combined work, and to convey the resulting work. The terms of this
|
|
558
557
|
License will continue to apply to the part which is the covered work,
|
|
559
|
-
but the
|
|
560
|
-
|
|
561
|
-
combination as such.
|
|
558
|
+
but the work with which it is combined will remain governed by version
|
|
559
|
+
3 of the GNU General Public License.
|
|
562
560
|
|
|
563
561
|
14. Revised Versions of this License.
|
|
564
562
|
|
|
565
563
|
The Free Software Foundation may publish revised and/or new versions of
|
|
566
|
-
the GNU General Public License from time to time. Such new versions
|
|
567
|
-
be similar in spirit to the present version, but may differ in detail to
|
|
564
|
+
the GNU Affero General Public License from time to time. Such new versions
|
|
565
|
+
will be similar in spirit to the present version, but may differ in detail to
|
|
568
566
|
address new problems or concerns.
|
|
569
567
|
|
|
570
568
|
Each version is given a distinguishing version number. If the
|
|
571
|
-
Program specifies that a certain numbered version of the GNU General
|
|
569
|
+
Program specifies that a certain numbered version of the GNU Affero General
|
|
572
570
|
Public License "or any later version" applies to it, you have the
|
|
573
571
|
option of following the terms and conditions either of that numbered
|
|
574
572
|
version or of any later version published by the Free Software
|
|
575
573
|
Foundation. If the Program does not specify a version number of the
|
|
576
|
-
GNU General Public License, you may choose any version ever published
|
|
574
|
+
GNU Affero General Public License, you may choose any version ever published
|
|
577
575
|
by the Free Software Foundation.
|
|
578
576
|
|
|
579
577
|
If the Program specifies that a proxy can decide which future
|
|
580
|
-
versions of the GNU General Public License can be used, that proxy's
|
|
578
|
+
versions of the GNU Affero General Public License can be used, that proxy's
|
|
581
579
|
public statement of acceptance of a version permanently authorizes you
|
|
582
580
|
to choose that version for the Program.
|
|
583
581
|
|
|
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
|
|
635
633
|
Copyright (C) <year> <name of author>
|
|
636
634
|
|
|
637
635
|
This program is free software: you can redistribute it and/or modify
|
|
638
|
-
it under the terms of the GNU General Public License as published by
|
|
636
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
639
637
|
the Free Software Foundation, either version 3 of the License, or
|
|
640
638
|
(at your option) any later version.
|
|
641
639
|
|
|
642
640
|
This program is distributed in the hope that it will be useful,
|
|
643
641
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
644
642
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
645
|
-
GNU General Public License for more details.
|
|
643
|
+
GNU Affero General Public License for more details.
|
|
646
644
|
|
|
647
|
-
You should have received a copy of the GNU General Public License
|
|
645
|
+
You should have received a copy of the GNU Affero General Public License
|
|
648
646
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
649
647
|
|
|
650
648
|
Also add information on how to contact you by electronic and paper mail.
|
|
651
649
|
|
|
652
|
-
If
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
661
|
-
parts of the General Public License. Of course, your program's commands
|
|
662
|
-
might be different; for a GUI interface, you would use an "about box".
|
|
650
|
+
If your software can interact with users remotely through a computer
|
|
651
|
+
network, you should also make sure that it provides a way for users to
|
|
652
|
+
get its source. For example, if your program is a web application, its
|
|
653
|
+
interface could display a "Source" link that leads users to an archive
|
|
654
|
+
of the code. There are many ways you could offer source, and different
|
|
655
|
+
solutions will be better for different programs; see section 13 for the
|
|
656
|
+
specific requirements.
|
|
663
657
|
|
|
664
658
|
You should also get your employer (if you work as a programmer) or school,
|
|
665
659
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
666
|
-
For more information on this, and how to apply and follow the GNU
|
|
660
|
+
For more information on this, and how to apply and follow the GNU AGPL, see
|
|
667
661
|
<https://www.gnu.org/licenses/>.
|
|
668
|
-
|
|
669
|
-
The GNU General Public License does not permit incorporating your program
|
|
670
|
-
into proprietary programs. If your program is a subroutine library, you
|
|
671
|
-
may consider it more useful to permit linking proprietary applications with
|
|
672
|
-
the library. If this is what you want to do, use the GNU Lesser General
|
|
673
|
-
Public License instead of this License. But first, please read
|
|
674
|
-
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
package/README.md
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-AGPL--3.0-a855f7?style=flat-square" alt="AGPL-3.0 license"></a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://discord.gg/f59rg2RwUT"><img src="https://img.shields.io/badge/Join%20NeoLabs-Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join the NeoLabs Discord"></a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
21
25
|
<p align="center">
|
|
22
26
|
<a href="https://github.com/NeoLabs-Systems/NeoAgent/releases/latest"><img alt="Android" src="https://img.shields.io/badge/Android-APK-3ddc84?style=flat-square&logo=android&logoColor=white"></a>
|
|
23
27
|
<a href="https://github.com/NeoLabs-Systems/NeoAgent/releases/latest"><img alt="iOS" src="https://img.shields.io/badge/iOS-coming_soon-lightgrey?style=flat-square&logo=apple&logoColor=white"></a>
|
|
@@ -106,5 +110,11 @@ for questions and [GitHub Issues](https://github.com/NeoLabs-Systems/NeoAgent/is
|
|
|
106
110
|
for reproducible bugs. Security reports belong in the process described by
|
|
107
111
|
[SECURITY.md](SECURITY.md).
|
|
108
112
|
|
|
109
|
-
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
NeoAgent is licensed under the
|
|
116
|
+
[GNU Affero General Public License v3.0 only](LICENSE). The
|
|
117
|
+
[licensing decision](docs/licensing.md) explains why AGPL remains the fit for
|
|
118
|
+
this networked, self-hosted project and compares it with MPL-2.0.
|
|
110
119
|
|
|
120
|
+
*Made with ❤️ by [Neo](https://github.com/neooriginal) · [NeoLabs Systems](https://github.com/NeoLabs-Systems)*
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Licensing decision
|
|
2
|
+
|
|
3
|
+
NeoAgent remains licensed under the **GNU Affero General Public License
|
|
4
|
+
version 3 only** (`AGPL-3.0-only`).
|
|
5
|
+
|
|
6
|
+
The package metadata and README already declared AGPL-3.0-only. The repository's
|
|
7
|
+
`LICENSE` file contained the standard GPLv3 text instead of the Affero GPLv3
|
|
8
|
+
text; that inconsistency has been corrected.
|
|
9
|
+
|
|
10
|
+
## Why retain AGPL
|
|
11
|
+
|
|
12
|
+
NeoAgent is primarily a networked, self-hosted service. AGPLv3 requires an
|
|
13
|
+
operator who modifies the program and makes that modified version available to
|
|
14
|
+
users over a network to offer those users the corresponding source. That
|
|
15
|
+
reciprocity matches the project's goal of keeping improvements to deployed
|
|
16
|
+
NeoAgent services available to their users and the community.
|
|
17
|
+
|
|
18
|
+
The Mozilla Public License 2.0 is a weaker, file-level copyleft license. Changes
|
|
19
|
+
to MPL-covered files remain under the MPL when distributed, while separate new
|
|
20
|
+
files can be combined into a larger work under different terms. That can make
|
|
21
|
+
commercial embedding and proprietary extensions easier, but it does not provide
|
|
22
|
+
AGPL's network-interaction source requirement.
|
|
23
|
+
|
|
24
|
+
| Consideration | AGPL-3.0-only | MPL-2.0 |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| Copyleft scope | Program-level | File-level |
|
|
27
|
+
| Modified hosted service | Must offer corresponding source to network users | Hosting alone does not trigger source distribution |
|
|
28
|
+
| Separate proprietary files | Generally constrained when part of the covered program | Permitted in a larger work when kept in separate files |
|
|
29
|
+
| Patent grant | Included | Included |
|
|
30
|
+
| Warranty disclaimer | Included | Included |
|
|
31
|
+
|
|
32
|
+
## Legal-risk considerations
|
|
33
|
+
|
|
34
|
+
Changing licenses does not remove copyright, provenance, patent, trademark, or
|
|
35
|
+
third-party dependency obligations. Relicensing existing material also requires
|
|
36
|
+
the necessary rights from its copyright holders. For those reasons, adopting
|
|
37
|
+
MPL-2.0 should be treated as a separate relicensing project if the project's
|
|
38
|
+
commercial-integration goals later outweigh AGPL's network reciprocity.
|
|
39
|
+
|
|
40
|
+
This document records the project decision and is not legal advice. The
|
|
41
|
+
authoritative terms are in the repository's root `LICENSE` file. Useful primary
|
|
42
|
+
references are Mozilla's [MPL 2.0 FAQ](https://www.mozilla.org/MPL/2.0/FAQ/) and
|
|
43
|
+
the GNU Project's
|
|
44
|
+
[explanation of the Affero GPL](https://www.gnu.org/licenses/why-affero-gpl.html).
|
|
@@ -36,6 +36,7 @@ import 'src/desktop_screen_capture.dart';
|
|
|
36
36
|
import 'src/diagnostics_logger.dart';
|
|
37
37
|
import 'src/health_bridge.dart';
|
|
38
38
|
import 'src/live_voice_capture.dart';
|
|
39
|
+
import 'src/local_runtime_paths.dart';
|
|
39
40
|
import 'src/messaging_access_summary.dart';
|
|
40
41
|
import 'src/oauth_launcher.dart';
|
|
41
42
|
import 'src/security/password_strength.dart';
|
|
@@ -253,9 +253,11 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
253
253
|
Process? _proc;
|
|
254
254
|
String? _errorMsg;
|
|
255
255
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
LocalRuntimePaths get _runtimePaths => LocalRuntimePaths.fromEnvironment(
|
|
257
|
+
Platform.environment,
|
|
258
|
+
isWindows: Platform.isWindows,
|
|
259
|
+
);
|
|
260
|
+
String get _home => _runtimePaths.homeDirectory;
|
|
259
261
|
|
|
260
262
|
@override
|
|
261
263
|
void initState() {
|
|
@@ -307,10 +309,10 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
bool _ensureEnvFile() {
|
|
310
|
-
final envPath =
|
|
312
|
+
final envPath = _runtimePaths.envFile;
|
|
311
313
|
if (!File(envPath).existsSync()) {
|
|
312
314
|
try {
|
|
313
|
-
Directory(
|
|
315
|
+
Directory(_runtimePaths.runtimeHome).createSync(recursive: true);
|
|
314
316
|
File(envPath).writeAsStringSync('NODE_ENV=production\nPORT=3333\n');
|
|
315
317
|
} catch (_) {
|
|
316
318
|
return false;
|
|
@@ -324,7 +326,7 @@ class _LocalInstallWidgetState extends State<_LocalInstallWidget> {
|
|
|
324
326
|
setState(() {
|
|
325
327
|
_phase = _LocalInstallPhase.failed;
|
|
326
328
|
_errorMsg =
|
|
327
|
-
'Could not create
|
|
329
|
+
'Could not create the NeoAgent runtime configuration — check directory permissions.';
|
|
328
330
|
});
|
|
329
331
|
return;
|
|
330
332
|
}
|
|
@@ -1504,13 +1504,19 @@ class NeoAgentController extends ChangeNotifier {
|
|
|
1504
1504
|
final sessionCookie = _backendClient.sessionCookie?.trim() ?? '';
|
|
1505
1505
|
final shouldPersistSession = isAuthenticated && sessionCookie.isNotEmpty;
|
|
1506
1506
|
if (shouldPersistSession) {
|
|
1507
|
+
var storedSecurely = false;
|
|
1507
1508
|
try {
|
|
1508
1509
|
await _secureStorage.write(
|
|
1509
1510
|
key: _sessionCookieSecureStorageKey,
|
|
1510
1511
|
value: sessionCookie,
|
|
1511
1512
|
);
|
|
1513
|
+
storedSecurely = true;
|
|
1512
1514
|
} catch (_) {}
|
|
1513
|
-
|
|
1515
|
+
if (storedSecurely) {
|
|
1516
|
+
await _prefs?.remove(_sessionCookiePrefsKey);
|
|
1517
|
+
} else {
|
|
1518
|
+
await _prefs?.setString(_sessionCookiePrefsKey, sessionCookie);
|
|
1519
|
+
}
|
|
1514
1520
|
await _prefs?.setString(_sessionCookieBackendPrefsKey, backendUrl);
|
|
1515
1521
|
await _syncDesktopCompanionSession();
|
|
1516
1522
|
unawaited(_syncHomeWidgetConfig());
|
|
@@ -186,10 +186,12 @@ class _ServerPanelState extends State<ServerPanel> {
|
|
|
186
186
|
bool _secureCookies = false;
|
|
187
187
|
bool _trustProxy = false;
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
LocalRuntimePaths get _runtimePaths => LocalRuntimePaths.fromEnvironment(
|
|
190
|
+
Platform.environment,
|
|
191
|
+
isWindows: Platform.isWindows,
|
|
192
|
+
);
|
|
193
|
+
String get _home => _runtimePaths.homeDirectory;
|
|
194
|
+
String get _envPath => _runtimePaths.envFile;
|
|
193
195
|
|
|
194
196
|
@override
|
|
195
197
|
void initState() {
|
|
@@ -275,7 +277,7 @@ class _ServerPanelState extends State<ServerPanel> {
|
|
|
275
277
|
if (Platform.isLinux) {
|
|
276
278
|
return File('$_home/.config/systemd/user/neoagent.service').existsSync();
|
|
277
279
|
}
|
|
278
|
-
return File(
|
|
280
|
+
return File(_runtimePaths.pidFile).existsSync();
|
|
279
281
|
}
|
|
280
282
|
|
|
281
283
|
Future<bool> _checkServiceRunning() async {
|
|
@@ -297,7 +299,7 @@ class _ServerPanelState extends State<ServerPanel> {
|
|
|
297
299
|
|
|
298
300
|
void _ensureEnvFile() {
|
|
299
301
|
if (!File(_envPath).existsSync()) {
|
|
300
|
-
Directory(
|
|
302
|
+
Directory(_runtimePaths.runtimeHome).createSync(recursive: true);
|
|
301
303
|
final port =
|
|
302
304
|
_portCtrl.text.trim().isEmpty ? '3333' : _portCtrl.text.trim();
|
|
303
305
|
final buf = StringBuffer()
|
|
@@ -843,7 +845,7 @@ class _ServerPanelState extends State<ServerPanel> {
|
|
|
843
845
|
}
|
|
844
846
|
|
|
845
847
|
Widget _buildLogsTab() {
|
|
846
|
-
final path =
|
|
848
|
+
final path = _runtimePaths.logFile;
|
|
847
849
|
final f = File(path);
|
|
848
850
|
String content;
|
|
849
851
|
if (f.existsSync()) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
class LocalRuntimePaths {
|
|
2
|
+
LocalRuntimePaths._({
|
|
3
|
+
required this.homeDirectory,
|
|
4
|
+
required this.runtimeHome,
|
|
5
|
+
required this.dataDirectory,
|
|
6
|
+
required this.envFile,
|
|
7
|
+
required this.separator,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
factory LocalRuntimePaths.fromEnvironment(
|
|
11
|
+
Map<String, String> environment, {
|
|
12
|
+
required bool isWindows,
|
|
13
|
+
}) {
|
|
14
|
+
final separator = isWindows ? r'\' : '/';
|
|
15
|
+
final homeKey = isWindows ? 'USERPROFILE' : 'HOME';
|
|
16
|
+
final homeDirectory = (environment[homeKey] ?? '').trim();
|
|
17
|
+
if (homeDirectory.isEmpty) {
|
|
18
|
+
throw StateError('$homeKey is required to locate NeoAgent runtime data.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
final configuredRuntimeHome = (environment['NEOAGENT_HOME'] ?? '').trim();
|
|
22
|
+
final runtimeHome = configuredRuntimeHome.isNotEmpty
|
|
23
|
+
? configuredRuntimeHome
|
|
24
|
+
: _join(homeDirectory, '.neoagent', separator);
|
|
25
|
+
final configuredDataDirectory = (environment['NEOAGENT_DATA_DIR'] ?? '')
|
|
26
|
+
.trim();
|
|
27
|
+
final dataDirectory = configuredDataDirectory.isNotEmpty
|
|
28
|
+
? configuredDataDirectory
|
|
29
|
+
: _join(runtimeHome, 'data', separator);
|
|
30
|
+
final configuredEnvFile = (environment['NEOAGENT_ENV_FILE'] ?? '').trim();
|
|
31
|
+
final envFile = configuredEnvFile.isNotEmpty
|
|
32
|
+
? configuredEnvFile
|
|
33
|
+
: _join(runtimeHome, '.env', separator);
|
|
34
|
+
|
|
35
|
+
return LocalRuntimePaths._(
|
|
36
|
+
homeDirectory: homeDirectory,
|
|
37
|
+
runtimeHome: runtimeHome,
|
|
38
|
+
dataDirectory: dataDirectory,
|
|
39
|
+
envFile: envFile,
|
|
40
|
+
separator: separator,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
final String homeDirectory;
|
|
45
|
+
final String runtimeHome;
|
|
46
|
+
final String dataDirectory;
|
|
47
|
+
final String envFile;
|
|
48
|
+
final String separator;
|
|
49
|
+
|
|
50
|
+
String get logDirectory => _join(dataDirectory, 'logs', separator);
|
|
51
|
+
String get logFile => _join(logDirectory, 'neoagent.log', separator);
|
|
52
|
+
String get pidFile => _join(dataDirectory, 'neoagent.pid', separator);
|
|
53
|
+
|
|
54
|
+
static String _join(String parent, String child, String separator) {
|
|
55
|
+
if (parent.endsWith('/') || parent.endsWith(r'\')) {
|
|
56
|
+
return '$parent$child';
|
|
57
|
+
}
|
|
58
|
+
return '$parent$separator$child';
|
|
59
|
+
}
|
|
60
|
+
}
|
package/lib/manager.js
CHANGED
|
@@ -575,6 +575,8 @@ function defaultEnvLines(current = {}) {
|
|
|
575
575
|
`NEOAGENT_VM_MEMORY_MB=${current.NEOAGENT_VM_MEMORY_MB || '4096'}`,
|
|
576
576
|
`NEOAGENT_VM_CPUS=${current.NEOAGENT_VM_CPUS || '2'}`,
|
|
577
577
|
`NEOAGENT_VM_GUEST_TOKEN=${current.NEOAGENT_VM_GUEST_TOKEN || randomSecret()}`,
|
|
578
|
+
`ADMIN_USERNAME=${current.ADMIN_USERNAME || 'admin'}`,
|
|
579
|
+
`ADMIN_PASSWORD=${current.ADMIN_PASSWORD || randomSecret()}`,
|
|
578
580
|
current.XAI_BASE_URL ? `XAI_BASE_URL=${current.XAI_BASE_URL}` : 'XAI_BASE_URL=https://api.x.ai/v1',
|
|
579
581
|
current.OLLAMA_URL ? `OLLAMA_URL=${current.OLLAMA_URL}` : 'OLLAMA_URL=http://localhost:11434',
|
|
580
582
|
current.DEEPGRAM_BASE_URL ? `DEEPGRAM_BASE_URL=${current.DEEPGRAM_BASE_URL}` : 'DEEPGRAM_BASE_URL=https://api.deepgram.com',
|
|
@@ -586,8 +588,13 @@ function defaultEnvLines(current = {}) {
|
|
|
586
588
|
function writeDefaultEnvFile() {
|
|
587
589
|
ensureRuntimeDirs();
|
|
588
590
|
const current = Object.fromEntries(parseEnv(readEnvFileRaw()).entries());
|
|
589
|
-
|
|
590
|
-
|
|
591
|
+
for (const line of defaultEnvLines(current)) {
|
|
592
|
+
const separatorIndex = line.indexOf('=');
|
|
593
|
+
const key = line.slice(0, separatorIndex);
|
|
594
|
+
if (String(current[key] || '').trim()) continue;
|
|
595
|
+
upsertEnvValue(key, line.slice(separatorIndex + 1));
|
|
596
|
+
}
|
|
597
|
+
logOk(`Ensured default config at ${ENV_FILE}`);
|
|
591
598
|
rememberInstallAction('Add provider keys with `neoagent setup`, `neoagent env set KEY VALUE`, or the login commands when you are ready.');
|
|
592
599
|
}
|
|
593
600
|
|
|
@@ -1560,6 +1567,7 @@ async function cmdInstall() {
|
|
|
1560
1567
|
writeDefaultEnvFile();
|
|
1561
1568
|
}
|
|
1562
1569
|
} else {
|
|
1570
|
+
writeDefaultEnvFile();
|
|
1563
1571
|
logOk(`Using config ${ENV_FILE}`);
|
|
1564
1572
|
}
|
|
1565
1573
|
|
package/package.json
CHANGED
package/runtime/paths.js
CHANGED
|
@@ -17,6 +17,15 @@ const PID_FILE = path.join(DATA_DIR, 'neoagent.pid');
|
|
|
17
17
|
const LEGACY_ENV_FILE = path.join(APP_DIR, '.env');
|
|
18
18
|
const LEGACY_DATA_DIR = path.join(APP_DIR, 'data');
|
|
19
19
|
const LEGACY_AGENT_DATA_DIR = path.join(APP_DIR, 'agent-data');
|
|
20
|
+
const LEGACY_DESKTOP_RUNTIME_DIR = path.join(HOME_DIR, '.neoagent', 'runtime');
|
|
21
|
+
const LEGACY_DESKTOP_ENV_FILE = path.join(LEGACY_DESKTOP_RUNTIME_DIR, '.env');
|
|
22
|
+
const PERSISTENT_IDENTITY_ENV_KEYS = new Set([
|
|
23
|
+
'SESSION_SECRET',
|
|
24
|
+
'NEOAGENT_ENCRYPTION_KEY',
|
|
25
|
+
'NEOAGENT_VM_GUEST_TOKEN',
|
|
26
|
+
'ADMIN_USERNAME',
|
|
27
|
+
'ADMIN_PASSWORD',
|
|
28
|
+
]);
|
|
20
29
|
const DEFAULT_VM_BASE_IMAGE_URLS = Object.freeze({
|
|
21
30
|
arm64: 'https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img',
|
|
22
31
|
x64: 'https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img',
|
|
@@ -47,6 +56,39 @@ function copyDirMerge(src, dest) {
|
|
|
47
56
|
return true;
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
function migrationBackupPath(sourcePath) {
|
|
60
|
+
const basePath = `${sourcePath}.migrated`;
|
|
61
|
+
if (!fs.existsSync(basePath)) return basePath;
|
|
62
|
+
return `${basePath}.${Date.now()}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function migrateLegacyDesktopEnv({
|
|
66
|
+
legacyEnvFile = LEGACY_DESKTOP_ENV_FILE,
|
|
67
|
+
envFile = ENV_FILE,
|
|
68
|
+
logger = () => {},
|
|
69
|
+
} = {}) {
|
|
70
|
+
if (path.resolve(legacyEnvFile) === path.resolve(envFile)) return false;
|
|
71
|
+
if (!fs.existsSync(legacyEnvFile)) return false;
|
|
72
|
+
|
|
73
|
+
const legacyValues = parseEnv(readEnvFileRaw(legacyEnvFile));
|
|
74
|
+
if (legacyValues.size === 0) return false;
|
|
75
|
+
|
|
76
|
+
const currentValues = parseEnv(readEnvFileRaw(envFile));
|
|
77
|
+
for (const [key, value] of legacyValues.entries()) {
|
|
78
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
|
|
79
|
+
const currentValue = String(currentValues.get(key) || '').trim();
|
|
80
|
+
if (PERSISTENT_IDENTITY_ENV_KEYS.has(key) && currentValue) continue;
|
|
81
|
+
upsertEnvValue(envFile, key, value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const backupPath = migrationBackupPath(legacyEnvFile);
|
|
85
|
+
fs.renameSync(legacyEnvFile, backupPath);
|
|
86
|
+
logger(
|
|
87
|
+
`migrated desktop config ${legacyEnvFile} -> ${envFile} (backup: ${backupPath})`,
|
|
88
|
+
);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
50
92
|
function migrateLegacyRuntime(logger = () => {}) {
|
|
51
93
|
ensureRuntimeDirs();
|
|
52
94
|
let changed = false;
|
|
@@ -89,6 +131,15 @@ function migrateLegacyRuntime(logger = () => {}) {
|
|
|
89
131
|
log(`migrated ${LEGACY_ENV_FILE} -> ${ENV_FILE}`);
|
|
90
132
|
changed = true;
|
|
91
133
|
}
|
|
134
|
+
try {
|
|
135
|
+
if (migrateLegacyDesktopEnv({ logger: log })) {
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logError(
|
|
140
|
+
`failed to migrate desktop runtime config ${LEGACY_DESKTOP_ENV_FILE} -> ${ENV_FILE}: ${error.message}`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
92
143
|
if (copyDirMerge(LEGACY_DATA_DIR, DATA_DIR)) {
|
|
93
144
|
log(`migrated ${LEGACY_DATA_DIR} -> ${DATA_DIR}`);
|
|
94
145
|
changed = true;
|
|
@@ -248,6 +299,22 @@ function ensureSecureRuntimeEnv({ envFile = ENV_FILE, env = process.env, logger
|
|
|
248
299
|
}
|
|
249
300
|
env.SESSION_SECRET = sessionSecret;
|
|
250
301
|
|
|
302
|
+
let adminUsername = String(env.ADMIN_USERNAME || parsed.get('ADMIN_USERNAME') || '').trim();
|
|
303
|
+
if (!adminUsername) {
|
|
304
|
+
adminUsername = 'admin';
|
|
305
|
+
upsertEnvValue(envFile, 'ADMIN_USERNAME', adminUsername);
|
|
306
|
+
changes.push('ADMIN_USERNAME');
|
|
307
|
+
}
|
|
308
|
+
env.ADMIN_USERNAME = adminUsername;
|
|
309
|
+
|
|
310
|
+
let adminPassword = String(env.ADMIN_PASSWORD || parsed.get('ADMIN_PASSWORD') || '').trim();
|
|
311
|
+
if (!adminPassword) {
|
|
312
|
+
adminPassword = generateSecret(16);
|
|
313
|
+
upsertEnvValue(envFile, 'ADMIN_PASSWORD', adminPassword);
|
|
314
|
+
changes.push('ADMIN_PASSWORD');
|
|
315
|
+
}
|
|
316
|
+
env.ADMIN_PASSWORD = adminPassword;
|
|
317
|
+
|
|
251
318
|
let guestToken = String(env.NEOAGENT_VM_GUEST_TOKEN || parsed.get('NEOAGENT_VM_GUEST_TOKEN') || '').trim();
|
|
252
319
|
if (!isValidVmGuestToken(guestToken)) {
|
|
253
320
|
guestToken = generateSecret(32);
|
|
@@ -285,10 +352,13 @@ module.exports = {
|
|
|
285
352
|
LEGACY_ENV_FILE,
|
|
286
353
|
LEGACY_DATA_DIR,
|
|
287
354
|
LEGACY_AGENT_DATA_DIR,
|
|
355
|
+
LEGACY_DESKTOP_RUNTIME_DIR,
|
|
356
|
+
LEGACY_DESKTOP_ENV_FILE,
|
|
288
357
|
DEFAULT_VM_BASE_IMAGE_URLS,
|
|
289
358
|
ensureRuntimeDirs,
|
|
290
359
|
ensureSecureRuntimeEnv,
|
|
291
360
|
getDefaultVmBaseImageUrl,
|
|
361
|
+
migrateLegacyDesktopEnv,
|
|
292
362
|
migrateLegacyRuntime,
|
|
293
363
|
removeEnvValue,
|
|
294
364
|
upsertEnvValue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ff1479dd5e8cd70881eb9d7023ff51d1
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"0cd610717bde95fd88343c64f81c11ba4e5c00
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2926330069" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|