safeclaw 0.1.1 → 0.1.3
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 +295 -0
- package/bin/safeclaw.js +0 -0
- package/dist/main.js +82 -82
- package/package.json +15 -15
package/LICENSE
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
6
|
+
of this license document, but changing it is not allowed.
|
|
7
|
+
|
|
8
|
+
Preamble
|
|
9
|
+
|
|
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.
|
|
13
|
+
|
|
14
|
+
The licenses for most software and other practical works are designed
|
|
15
|
+
to take away your freedom to share and change the works. By contrast,
|
|
16
|
+
the GNU General Public Licenses are intended to guarantee your freedom to
|
|
17
|
+
share and change all versions of a program--to make sure it remains free
|
|
18
|
+
software for all its users.
|
|
19
|
+
|
|
20
|
+
When we speak of free software, we are referring to freedom, not
|
|
21
|
+
price. Our General Public Licenses are designed to make sure that you
|
|
22
|
+
have the freedom to distribute copies of free software (and charge for
|
|
23
|
+
them if you wish), that you receive source code or can get it if you
|
|
24
|
+
want it, that you can change the software or use pieces of it in new
|
|
25
|
+
free programs, and that you know you can do these things.
|
|
26
|
+
|
|
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.
|
|
55
|
+
|
|
56
|
+
The precise terms and conditions for copying, distribution and
|
|
57
|
+
modification follow.
|
|
58
|
+
|
|
59
|
+
TERMS AND CONDITIONS
|
|
60
|
+
|
|
61
|
+
0. Definitions.
|
|
62
|
+
|
|
63
|
+
"This License" refers to version 3 of the GNU Affero General Public License.
|
|
64
|
+
|
|
65
|
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
|
66
|
+
works, such as semiconductor masks.
|
|
67
|
+
|
|
68
|
+
"The Program" refers to any copyrightable work licensed under this
|
|
69
|
+
License. Each licensee is addressed as "you". "Licensees" and
|
|
70
|
+
"recipients" may be individuals or organizations.
|
|
71
|
+
|
|
72
|
+
To "modify" a work means to copy from or adapt all or part of the work
|
|
73
|
+
in a fashion requiring copyright permission, other than the making of an
|
|
74
|
+
exact copy. The resulting work is called a "modified version" of the
|
|
75
|
+
earlier work or a work "based on" the earlier work.
|
|
76
|
+
|
|
77
|
+
A "covered work" means either the unmodified Program or a work based
|
|
78
|
+
on the Program.
|
|
79
|
+
|
|
80
|
+
To "propagate" a work means to do anything with it that, without
|
|
81
|
+
permission, would make you directly or secondarily liable for
|
|
82
|
+
infringement under applicable copyright law, except executing it on a
|
|
83
|
+
computer or modifying a private copy. Propagation includes copying,
|
|
84
|
+
distribution (with or without modification), making available to the
|
|
85
|
+
public, and in some countries other activities as well.
|
|
86
|
+
|
|
87
|
+
To "convey" a work means any kind of propagation that enables other
|
|
88
|
+
parties to make or receive copies. Mere interaction with a user through
|
|
89
|
+
a computer network, with no transfer of a copy, is not conveying.
|
|
90
|
+
|
|
91
|
+
An interactive user interface displays "Appropriate Legal Notices"
|
|
92
|
+
to the extent that it includes a convenient and prominently visible
|
|
93
|
+
feature that (1) displays an appropriate copyright notice, and (2)
|
|
94
|
+
tells the user that there is no warranty for the work (except to the
|
|
95
|
+
extent that warranties are provided), that licensees may convey the
|
|
96
|
+
work under this License, and how to view a copy of this License. If
|
|
97
|
+
the interface presents a list of user commands or options, such as a
|
|
98
|
+
menu, a prominent item in the list meets this criterion.
|
|
99
|
+
|
|
100
|
+
1. Source Code.
|
|
101
|
+
|
|
102
|
+
The "source code" for a work means the preferred form of the work
|
|
103
|
+
for making modifications to it. "Object code" means any non-source
|
|
104
|
+
form of a work.
|
|
105
|
+
|
|
106
|
+
A "Standard Interface" means an interface that either is an official
|
|
107
|
+
standard defined by a recognized standards body, or, in the case of
|
|
108
|
+
interfaces specified for a particular programming language, one that
|
|
109
|
+
is widely used among developers working in that language.
|
|
110
|
+
|
|
111
|
+
The "System Libraries" of an executable work include anything, other
|
|
112
|
+
than the work as a whole, that (a) is included in the normal form of
|
|
113
|
+
packaging a Major Component, but which is not part of that Major
|
|
114
|
+
Component, and (b) serves only to enable use of the work with that
|
|
115
|
+
Major Component, or to implement a Standard Interface for which an
|
|
116
|
+
implementation is available to the public in source code form. A
|
|
117
|
+
"Major Component", in this context, means a major essential component
|
|
118
|
+
(kernel, window system, and so on) of the specific operating system
|
|
119
|
+
(if any) on which the executable work runs, or a compiler used to
|
|
120
|
+
produce the work, or an object code interpreter used to run it.
|
|
121
|
+
|
|
122
|
+
The "Corresponding Source" for a work in object code form means all
|
|
123
|
+
the source code needed to generate, install, and (for an executable
|
|
124
|
+
work) run the object code and to modify the work, including scripts to
|
|
125
|
+
control those activities. However, it does not include the work's
|
|
126
|
+
System Libraries, or general-purpose tools or generally available free
|
|
127
|
+
programs which are used unmodified in performing those activities but
|
|
128
|
+
which are not part of the work. For example, Corresponding Source
|
|
129
|
+
includes interface definition files associated with source files for
|
|
130
|
+
the work, and the source code for shared libraries and dynamically
|
|
131
|
+
linked subprograms that the work is specifically designed to require,
|
|
132
|
+
such as by intimate data communication or control flow between those
|
|
133
|
+
subprograms and other parts of the work.
|
|
134
|
+
|
|
135
|
+
The Corresponding Source need not include anything that users
|
|
136
|
+
can regenerate automatically from other parts of the Corresponding
|
|
137
|
+
Source.
|
|
138
|
+
|
|
139
|
+
The Corresponding Source for a work in source code form is that
|
|
140
|
+
same work.
|
|
141
|
+
|
|
142
|
+
2. Basic Permissions.
|
|
143
|
+
|
|
144
|
+
All rights granted under this License are granted for the term of
|
|
145
|
+
copyright on the Program, and are irrevocable provided the stated
|
|
146
|
+
conditions are met. This License explicitly affirms your unlimited
|
|
147
|
+
permission to run the unmodified Program. The output from running a
|
|
148
|
+
covered work is covered by this License only if the output, given its
|
|
149
|
+
content, constitutes a covered work. This License acknowledges your
|
|
150
|
+
rights of fair use or other equivalent, as provided by copyright law.
|
|
151
|
+
|
|
152
|
+
You may make, run and propagate covered works that you do not
|
|
153
|
+
convey, without conditions so long as your license otherwise remains
|
|
154
|
+
in force. You may convey covered works to others for the sole purpose
|
|
155
|
+
of having them make modifications exclusively for you, or provide you
|
|
156
|
+
with facilities for running those works, provided that you comply with
|
|
157
|
+
the terms of this License in conveying all material for which you do
|
|
158
|
+
not control copyright. Those thus making or running the covered works
|
|
159
|
+
for you must do so exclusively on your behalf, under your direction
|
|
160
|
+
and control, on terms that prohibit them from making any copies of
|
|
161
|
+
your copyrighted material outside their relationship with you.
|
|
162
|
+
|
|
163
|
+
Conveying under any other circumstances is permitted solely under
|
|
164
|
+
the conditions stated below. Sublicensing is not allowed; section 10
|
|
165
|
+
makes it unnecessary.
|
|
166
|
+
|
|
167
|
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
|
168
|
+
|
|
169
|
+
No covered work shall be deemed part of an effective technological
|
|
170
|
+
measure under any applicable law fulfilling obligations under article
|
|
171
|
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
|
172
|
+
similar laws prohibiting or restricting circumvention of such
|
|
173
|
+
measures.
|
|
174
|
+
|
|
175
|
+
When you convey a covered work, you waive any legal power to forbid
|
|
176
|
+
circumvention of technological measures to the extent such circumvention
|
|
177
|
+
is effected by exercising rights under this License with respect to
|
|
178
|
+
the covered work, and you disclaim any intention to limit operation or
|
|
179
|
+
modification of the work as a means of enforcing, against the work's
|
|
180
|
+
users, your or third parties' legal rights to forbid circumvention of
|
|
181
|
+
technological measures.
|
|
182
|
+
|
|
183
|
+
4. Conveying Verbatim Copies.
|
|
184
|
+
|
|
185
|
+
You may convey verbatim copies of the Program's source code as you
|
|
186
|
+
receive it, in any medium, provided that you conspicuously and
|
|
187
|
+
appropriately publish on each copy an appropriate copyright notice;
|
|
188
|
+
keep intact all notices stating that this License and any
|
|
189
|
+
non-permissive terms added in accord with section 7 apply to the code;
|
|
190
|
+
keep intact all notices of the absence of any warranty; and give all
|
|
191
|
+
recipients a copy of this License along with the Program.
|
|
192
|
+
|
|
193
|
+
You may charge any price or no price for each copy that you convey,
|
|
194
|
+
and you may offer support or warranty protection for a fee.
|
|
195
|
+
|
|
196
|
+
5. Conveying Modified Source Versions.
|
|
197
|
+
|
|
198
|
+
You may convey a work based on the Program, or the modifications to
|
|
199
|
+
produce it from the Program, in the form of source code under the
|
|
200
|
+
terms of section 4, provided that you also meet all of these conditions:
|
|
201
|
+
|
|
202
|
+
a) The work must carry prominent notices stating that you modified
|
|
203
|
+
it, and giving a relevant date.
|
|
204
|
+
|
|
205
|
+
b) The work must carry prominent notices stating that it is
|
|
206
|
+
released under this License and any conditions added under section
|
|
207
|
+
7. This requirement modifies the requirement in section 4 to
|
|
208
|
+
"keep intact all notices".
|
|
209
|
+
|
|
210
|
+
c) You must license the entire work, as a whole, under this
|
|
211
|
+
License to anyone who comes into possession of a copy. This
|
|
212
|
+
License will therefore apply, along with any applicable section 7
|
|
213
|
+
additional terms, to the whole of the work, and all its parts,
|
|
214
|
+
regardless of how they are packaged. This License gives no
|
|
215
|
+
permission to license the work in any other way, but it does not
|
|
216
|
+
invalidate such permission if you have separately received it.
|
|
217
|
+
|
|
218
|
+
d) If the work has interactive user interfaces, each must display
|
|
219
|
+
Appropriate Legal Notices; however, if the Program has interactive
|
|
220
|
+
interfaces that do not display Appropriate Legal Notices, your
|
|
221
|
+
work need not make them do so.
|
|
222
|
+
|
|
223
|
+
A compilation of a covered work with other separate and independent
|
|
224
|
+
works, which are not by their nature extensions of the covered work,
|
|
225
|
+
and which are not combined with it such as to form a larger program,
|
|
226
|
+
in or on a volume of a storage or distribution medium, is called an
|
|
227
|
+
"aggregate" if the compilation and its resulting copyright are not
|
|
228
|
+
used to limit the access or legal rights of the compilation's users
|
|
229
|
+
beyond what the individual works permit. Inclusion of a covered work
|
|
230
|
+
in an aggregate does not cause this License to apply to the other
|
|
231
|
+
parts of the aggregate.
|
|
232
|
+
|
|
233
|
+
6. Conveying Non-Source Forms.
|
|
234
|
+
|
|
235
|
+
You may convey a covered work in object code form under the terms
|
|
236
|
+
of sections 4 and 5, provided that you also convey the
|
|
237
|
+
machine-readable Corresponding Source under the terms of this License,
|
|
238
|
+
in one of these ways:
|
|
239
|
+
|
|
240
|
+
a) Convey the object code in, or embodied in, a physical product
|
|
241
|
+
(including a physical distribution medium), accompanied by the
|
|
242
|
+
Corresponding Source fixed on a durable physical medium
|
|
243
|
+
customarily used for software interchange.
|
|
244
|
+
|
|
245
|
+
b) Convey the object code in, or embodied in, a physical product
|
|
246
|
+
(including a physical distribution medium), accompanied by a
|
|
247
|
+
written offer, valid for at least three years and valid for as
|
|
248
|
+
long as you offer spare parts or customer support for that product
|
|
249
|
+
model, to give anyone who possesses the object code either (1) a
|
|
250
|
+
copy of the Corresponding Source for all the software in the
|
|
251
|
+
product that is covered by this License, on a durable physical
|
|
252
|
+
medium customarily used for software interchange, for a price no
|
|
253
|
+
more than your reasonable cost of physically performing this
|
|
254
|
+
conveying of source, or (2) access to copy the
|
|
255
|
+
Corresponding Source from a network server at no charge.
|
|
256
|
+
|
|
257
|
+
c) Convey individual copies of the object code with a copy of the
|
|
258
|
+
written offer to provide the Corresponding Source. This
|
|
259
|
+
alternative is allowed only occasionally and noncommercially, and
|
|
260
|
+
only if you received the object code with such an offer, in accord
|
|
261
|
+
with subsection 6b.
|
|
262
|
+
|
|
263
|
+
d) Convey the object code by offering access from a designated
|
|
264
|
+
place (gratis or for a charge), and offer equivalent access to the
|
|
265
|
+
Corresponding Source in the same way through the same place at no
|
|
266
|
+
further charge. You need not require recipients to copy the
|
|
267
|
+
Corresponding Source along with the object code. If the place to
|
|
268
|
+
copy the object code is a network server, the Corresponding Source
|
|
269
|
+
may be on a different server (operated by you or a third party)
|
|
270
|
+
that supports equivalent copying facilities, provided you maintain
|
|
271
|
+
clear directions next to the object code saying where to find the
|
|
272
|
+
Corresponding Source. Regardless of what server hosts the
|
|
273
|
+
Corresponding Source, you remain obligated to ensure that it is
|
|
274
|
+
available for as long as needed to satisfy these requirements.
|
|
275
|
+
|
|
276
|
+
e) Convey the object code using peer-to-peer transmission, provided
|
|
277
|
+
you inform other peers where the object code and Corresponding
|
|
278
|
+
Source of the work are being offered to the general public at no
|
|
279
|
+
charge under subsection 6d.
|
|
280
|
+
|
|
281
|
+
A separable portion of the object code, whose source code is excluded
|
|
282
|
+
from the Corresponding Source as a System Library, need not be
|
|
283
|
+
included in conveying the object code work.
|
|
284
|
+
|
|
285
|
+
A "User Product" is either (1) a "consumer product", which means any
|
|
286
|
+
tangible personal property which is normally used for personal, family,
|
|
287
|
+
or household purposes, or (2) anything designed or sold for incorporation
|
|
288
|
+
into a dwelling. In determining whether a product is a consumer product,
|
|
289
|
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
|
290
|
+
product received by a particular user, "normally used" refers to a
|
|
291
|
+
typical or common use of that class of product, regardless of the status
|
|
292
|
+
of the particular user or of the way in which the particular user
|
|
293
|
+
actually uses, or expects or is expected to use, the product. A product
|
|
294
|
+
is a consumer product regardless of whether the product has substantial
|
|
295
|
+
commercial, industrial
|
package/bin/safeclaw.js
CHANGED
|
File without changes
|
package/dist/main.js
CHANGED
|
@@ -8,18 +8,16 @@ var __export = (target, all) => {
|
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/lib/version.ts
|
|
11
|
-
|
|
12
|
-
var require2 = createRequire(import.meta.url);
|
|
13
|
-
var pkg = require2("../../package.json");
|
|
14
|
-
var VERSION = pkg.version;
|
|
11
|
+
var VERSION = "0.1.3";
|
|
15
12
|
|
|
16
13
|
// src/server/index.ts
|
|
17
14
|
import Fastify from "fastify";
|
|
18
15
|
import fastifyStatic from "@fastify/static";
|
|
19
16
|
import fastifyCors from "@fastify/cors";
|
|
20
|
-
import
|
|
17
|
+
import fs8 from "fs";
|
|
21
18
|
|
|
22
19
|
// src/lib/paths.ts
|
|
20
|
+
import fs from "fs";
|
|
23
21
|
import path from "path";
|
|
24
22
|
import os from "os";
|
|
25
23
|
var HOME = os.homedir();
|
|
@@ -36,7 +34,9 @@ var OPENCLAW_DEVICE_AUTH_JSON = path.join(OPENCLAW_IDENTITY_DIR, "device-auth.js
|
|
|
36
34
|
var OPENCLAW_EXEC_APPROVALS_PATH = path.join(OPENCLAW_DIR, "exec-approvals.json");
|
|
37
35
|
function getPublicDir() {
|
|
38
36
|
const currentDir = path.dirname(new URL(import.meta.url).pathname);
|
|
39
|
-
|
|
37
|
+
const bundledPath = path.resolve(currentDir, "..", "public");
|
|
38
|
+
const devPath = path.resolve(currentDir, "..", "..", "public");
|
|
39
|
+
return fs.existsSync(bundledPath) ? bundledPath : devPath;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// src/server/socket.ts
|
|
@@ -47,7 +47,7 @@ import Database from "better-sqlite3";
|
|
|
47
47
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
48
48
|
|
|
49
49
|
// src/lib/config.ts
|
|
50
|
-
import
|
|
50
|
+
import fs2 from "fs";
|
|
51
51
|
|
|
52
52
|
// ../../packages/shared/dist/schemas.js
|
|
53
53
|
import { z } from "zod";
|
|
@@ -282,25 +282,25 @@ var DEFAULT_CONFIG = {
|
|
|
282
282
|
userId: null
|
|
283
283
|
};
|
|
284
284
|
function ensureDataDir() {
|
|
285
|
-
if (!
|
|
286
|
-
|
|
285
|
+
if (!fs2.existsSync(SAFECLAW_DIR)) {
|
|
286
|
+
fs2.mkdirSync(SAFECLAW_DIR, { recursive: true });
|
|
287
287
|
}
|
|
288
|
-
if (!
|
|
289
|
-
|
|
288
|
+
if (!fs2.existsSync(LOGS_DIR)) {
|
|
289
|
+
fs2.mkdirSync(LOGS_DIR, { recursive: true });
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
function readConfig() {
|
|
293
293
|
ensureDataDir();
|
|
294
|
-
if (!
|
|
295
|
-
|
|
294
|
+
if (!fs2.existsSync(CONFIG_PATH)) {
|
|
295
|
+
fs2.writeFileSync(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
296
296
|
return DEFAULT_CONFIG;
|
|
297
297
|
}
|
|
298
|
-
const raw = JSON.parse(
|
|
298
|
+
const raw = JSON.parse(fs2.readFileSync(CONFIG_PATH, "utf-8"));
|
|
299
299
|
return safeClawConfigSchema.parse(raw);
|
|
300
300
|
}
|
|
301
301
|
function writeConfig(config) {
|
|
302
302
|
ensureDataDir();
|
|
303
|
-
|
|
303
|
+
fs2.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
304
304
|
}
|
|
305
305
|
function resetConfig() {
|
|
306
306
|
writeConfig(DEFAULT_CONFIG);
|
|
@@ -426,9 +426,9 @@ import { eq as eq4, desc as desc3 } from "drizzle-orm";
|
|
|
426
426
|
|
|
427
427
|
// src/lib/logger.ts
|
|
428
428
|
import pino from "pino";
|
|
429
|
-
import
|
|
430
|
-
if (!
|
|
431
|
-
|
|
429
|
+
import fs3 from "fs";
|
|
430
|
+
if (!fs3.existsSync(LOGS_DIR)) {
|
|
431
|
+
fs3.mkdirSync(LOGS_DIR, { recursive: true });
|
|
432
432
|
}
|
|
433
433
|
function createLogger(consoleLevel = "info") {
|
|
434
434
|
return pino({
|
|
@@ -457,19 +457,19 @@ function setVerbose(verbose) {
|
|
|
457
457
|
}
|
|
458
458
|
|
|
459
459
|
// src/lib/openclaw-config.ts
|
|
460
|
-
import
|
|
460
|
+
import fs4 from "fs";
|
|
461
461
|
function readOpenClawConfig() {
|
|
462
|
-
if (!
|
|
462
|
+
if (!fs4.existsSync(OPENCLAW_CONFIG_PATH)) {
|
|
463
463
|
return null;
|
|
464
464
|
}
|
|
465
|
-
const raw = JSON.parse(
|
|
465
|
+
const raw = JSON.parse(fs4.readFileSync(OPENCLAW_CONFIG_PATH, "utf-8"));
|
|
466
466
|
return openClawConfigSchema.parse(raw);
|
|
467
467
|
}
|
|
468
468
|
function writeOpenClawConfig(updates) {
|
|
469
469
|
const current = readOpenClawConfig();
|
|
470
470
|
if (!current) return null;
|
|
471
471
|
const merged = deepMerge(current, updates);
|
|
472
|
-
|
|
472
|
+
fs4.writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(merged, null, 2));
|
|
473
473
|
return merged;
|
|
474
474
|
}
|
|
475
475
|
function deepMerge(target, source) {
|
|
@@ -489,11 +489,11 @@ function deepMerge(target, source) {
|
|
|
489
489
|
// src/lib/openclaw-client.ts
|
|
490
490
|
import { EventEmitter } from "events";
|
|
491
491
|
import crypto from "crypto";
|
|
492
|
-
import
|
|
492
|
+
import fs5 from "fs";
|
|
493
493
|
import { WebSocket } from "ws";
|
|
494
494
|
function readDeviceIdentity() {
|
|
495
495
|
try {
|
|
496
|
-
const raw =
|
|
496
|
+
const raw = fs5.readFileSync(OPENCLAW_DEVICE_JSON, "utf-8");
|
|
497
497
|
const data = JSON.parse(raw);
|
|
498
498
|
if (!data.deviceId || !data.publicKeyPem || !data.privateKeyPem) return null;
|
|
499
499
|
return {
|
|
@@ -1011,7 +1011,7 @@ var OpenClawClient = class extends EventEmitter {
|
|
|
1011
1011
|
|
|
1012
1012
|
// src/services/session-watcher.ts
|
|
1013
1013
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
1014
|
-
import
|
|
1014
|
+
import fs6 from "fs";
|
|
1015
1015
|
import path2 from "path";
|
|
1016
1016
|
var MAX_CONTENT_PREVIEW = 10 * 1024;
|
|
1017
1017
|
var SessionWatcher = class extends EventEmitter2 {
|
|
@@ -1030,7 +1030,7 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1030
1030
|
start() {
|
|
1031
1031
|
if (this.destroyed) return;
|
|
1032
1032
|
const agentsDir = path2.join(OPENCLAW_DIR, "agents");
|
|
1033
|
-
if (!
|
|
1033
|
+
if (!fs6.existsSync(agentsDir)) {
|
|
1034
1034
|
logger.debug("OpenClaw agents directory not found, will retry");
|
|
1035
1035
|
}
|
|
1036
1036
|
this.scanForSessions();
|
|
@@ -1038,8 +1038,8 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1038
1038
|
if (!this.destroyed) this.scanForSessions();
|
|
1039
1039
|
}, 1e4);
|
|
1040
1040
|
try {
|
|
1041
|
-
if (
|
|
1042
|
-
this.agentsDirWatcher =
|
|
1041
|
+
if (fs6.existsSync(agentsDir)) {
|
|
1042
|
+
this.agentsDirWatcher = fs6.watch(agentsDir, { recursive: true }, () => {
|
|
1043
1043
|
if (!this.destroyed) this.scanForSessions();
|
|
1044
1044
|
});
|
|
1045
1045
|
}
|
|
@@ -1067,10 +1067,10 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1067
1067
|
}
|
|
1068
1068
|
scanForSessions() {
|
|
1069
1069
|
const agentsDir = path2.join(OPENCLAW_DIR, "agents");
|
|
1070
|
-
if (!
|
|
1070
|
+
if (!fs6.existsSync(agentsDir)) return;
|
|
1071
1071
|
try {
|
|
1072
|
-
const agentNames =
|
|
1073
|
-
const stat =
|
|
1072
|
+
const agentNames = fs6.readdirSync(agentsDir).filter((name) => {
|
|
1073
|
+
const stat = fs6.statSync(path2.join(agentsDir, name));
|
|
1074
1074
|
return stat.isDirectory();
|
|
1075
1075
|
});
|
|
1076
1076
|
for (const agentName of agentNames) {
|
|
@@ -1082,11 +1082,11 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1082
1082
|
}
|
|
1083
1083
|
discoverSessionFiles(agentName) {
|
|
1084
1084
|
const sessionsDir = path2.join(OPENCLAW_DIR, "agents", agentName, "sessions");
|
|
1085
|
-
if (!
|
|
1085
|
+
if (!fs6.existsSync(sessionsDir)) return;
|
|
1086
1086
|
const sessionsJsonPath = path2.join(sessionsDir, "sessions.json");
|
|
1087
|
-
if (
|
|
1087
|
+
if (fs6.existsSync(sessionsJsonPath)) {
|
|
1088
1088
|
try {
|
|
1089
|
-
const raw =
|
|
1089
|
+
const raw = fs6.readFileSync(sessionsJsonPath, "utf-8");
|
|
1090
1090
|
const sessionsData = JSON.parse(raw);
|
|
1091
1091
|
const sessions2 = sessionsData.sessions ?? [];
|
|
1092
1092
|
for (const session of sessions2) {
|
|
@@ -1098,7 +1098,7 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1098
1098
|
}
|
|
1099
1099
|
}
|
|
1100
1100
|
try {
|
|
1101
|
-
const files =
|
|
1101
|
+
const files = fs6.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
1102
1102
|
for (const file of files) {
|
|
1103
1103
|
const filePath = path2.join(sessionsDir, file);
|
|
1104
1104
|
const sessionId = path2.basename(file, ".jsonl");
|
|
@@ -1109,8 +1109,8 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
watchSessionFile(filePath, sessionId, agentName) {
|
|
1111
1111
|
if (this.watchedFiles.has(filePath)) return;
|
|
1112
|
-
if (!
|
|
1113
|
-
const stat =
|
|
1112
|
+
if (!fs6.existsSync(filePath)) return;
|
|
1113
|
+
const stat = fs6.statSync(filePath);
|
|
1114
1114
|
const watched = {
|
|
1115
1115
|
path: filePath,
|
|
1116
1116
|
position: stat.size,
|
|
@@ -1120,7 +1120,7 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1120
1120
|
agentName
|
|
1121
1121
|
};
|
|
1122
1122
|
try {
|
|
1123
|
-
watched.watcher =
|
|
1123
|
+
watched.watcher = fs6.watch(filePath, () => {
|
|
1124
1124
|
if (!this.destroyed) {
|
|
1125
1125
|
this.readNewEntries(watched);
|
|
1126
1126
|
}
|
|
@@ -1134,13 +1134,13 @@ var SessionWatcher = class extends EventEmitter2 {
|
|
|
1134
1134
|
}
|
|
1135
1135
|
readNewEntries(watched) {
|
|
1136
1136
|
try {
|
|
1137
|
-
const stat =
|
|
1137
|
+
const stat = fs6.statSync(watched.path);
|
|
1138
1138
|
if (stat.size <= watched.position) return;
|
|
1139
|
-
const fd =
|
|
1139
|
+
const fd = fs6.openSync(watched.path, "r");
|
|
1140
1140
|
const bufferSize = stat.size - watched.position;
|
|
1141
1141
|
const buffer = Buffer.alloc(bufferSize);
|
|
1142
|
-
|
|
1143
|
-
|
|
1142
|
+
fs6.readSync(fd, buffer, 0, bufferSize, watched.position);
|
|
1143
|
+
fs6.closeSync(fd);
|
|
1144
1144
|
watched.position = stat.size;
|
|
1145
1145
|
const text2 = buffer.toString("utf-8");
|
|
1146
1146
|
const lines = text2.split("\n").filter((l) => l.trim());
|
|
@@ -1694,11 +1694,11 @@ function createExecApprovalService(client, io2, timeoutMs) {
|
|
|
1694
1694
|
}
|
|
1695
1695
|
|
|
1696
1696
|
// src/lib/exec-approvals-config.ts
|
|
1697
|
-
import
|
|
1697
|
+
import fs7 from "fs";
|
|
1698
1698
|
function readExecApprovalsConfig() {
|
|
1699
1699
|
try {
|
|
1700
|
-
if (!
|
|
1701
|
-
const raw =
|
|
1700
|
+
if (!fs7.existsSync(OPENCLAW_EXEC_APPROVALS_PATH)) return null;
|
|
1701
|
+
const raw = fs7.readFileSync(OPENCLAW_EXEC_APPROVALS_PATH, "utf-8");
|
|
1702
1702
|
return JSON.parse(raw);
|
|
1703
1703
|
} catch (err) {
|
|
1704
1704
|
logger.warn({ err }, "Failed to read exec-approvals.json");
|
|
@@ -1706,7 +1706,7 @@ function readExecApprovalsConfig() {
|
|
|
1706
1706
|
}
|
|
1707
1707
|
}
|
|
1708
1708
|
function writeExecApprovalsConfig(config) {
|
|
1709
|
-
|
|
1709
|
+
fs7.writeFileSync(
|
|
1710
1710
|
OPENCLAW_EXEC_APPROVALS_PATH,
|
|
1711
1711
|
JSON.stringify(config, null, 2)
|
|
1712
1712
|
);
|
|
@@ -3426,7 +3426,7 @@ async function createAppServer(port) {
|
|
|
3426
3426
|
await app.register(fastifyCors, { origin: "*" });
|
|
3427
3427
|
await registerRoutes(app);
|
|
3428
3428
|
const publicDir = getPublicDir();
|
|
3429
|
-
if (
|
|
3429
|
+
if (fs8.existsSync(publicDir) && fs8.readdirSync(publicDir).filter((f) => f !== ".gitkeep").length > 0) {
|
|
3430
3430
|
await app.register(fastifyStatic, {
|
|
3431
3431
|
root: publicDir,
|
|
3432
3432
|
prefix: "/",
|
|
@@ -3688,7 +3688,7 @@ Permission denied for port ${port}.
|
|
|
3688
3688
|
}
|
|
3689
3689
|
|
|
3690
3690
|
// src/commands/reset.ts
|
|
3691
|
-
import
|
|
3691
|
+
import fs9 from "fs";
|
|
3692
3692
|
import readline from "readline/promises";
|
|
3693
3693
|
import pc3 from "picocolors";
|
|
3694
3694
|
async function resetCommand(options) {
|
|
@@ -3707,12 +3707,12 @@ async function resetCommand(options) {
|
|
|
3707
3707
|
}
|
|
3708
3708
|
}
|
|
3709
3709
|
console.log(pc3.bold("Resetting SafeClaw..."));
|
|
3710
|
-
if (
|
|
3711
|
-
|
|
3710
|
+
if (fs9.existsSync(DB_PATH)) {
|
|
3711
|
+
fs9.unlinkSync(DB_PATH);
|
|
3712
3712
|
const walPath = DB_PATH + "-wal";
|
|
3713
3713
|
const shmPath = DB_PATH + "-shm";
|
|
3714
|
-
if (
|
|
3715
|
-
if (
|
|
3714
|
+
if (fs9.existsSync(walPath)) fs9.unlinkSync(walPath);
|
|
3715
|
+
if (fs9.existsSync(shmPath)) fs9.unlinkSync(shmPath);
|
|
3716
3716
|
console.log(pc3.green(" Database deleted."));
|
|
3717
3717
|
} else {
|
|
3718
3718
|
console.log(pc3.dim(" No database found, skipping."));
|
|
@@ -3725,11 +3725,11 @@ async function resetCommand(options) {
|
|
|
3725
3725
|
}
|
|
3726
3726
|
|
|
3727
3727
|
// src/commands/status.ts
|
|
3728
|
-
import
|
|
3728
|
+
import fs10 from "fs";
|
|
3729
3729
|
import Database3 from "better-sqlite3";
|
|
3730
3730
|
import pc4 from "picocolors";
|
|
3731
3731
|
async function statusCommand(options) {
|
|
3732
|
-
const exists =
|
|
3732
|
+
const exists = fs10.existsSync(SAFECLAW_DIR);
|
|
3733
3733
|
if (!exists) {
|
|
3734
3734
|
if (options.json) {
|
|
3735
3735
|
console.log(JSON.stringify({ initialized: false }, null, 2));
|
|
@@ -3743,7 +3743,7 @@ async function statusCommand(options) {
|
|
|
3743
3743
|
const config = readConfig();
|
|
3744
3744
|
let logCount = 0;
|
|
3745
3745
|
let activityCount = 0;
|
|
3746
|
-
const dbExists =
|
|
3746
|
+
const dbExists = fs10.existsSync(DB_PATH);
|
|
3747
3747
|
if (dbExists) {
|
|
3748
3748
|
try {
|
|
3749
3749
|
const sqlite = new Database3(DB_PATH, { readonly: true });
|
|
@@ -3757,14 +3757,14 @@ async function statusCommand(options) {
|
|
|
3757
3757
|
activityCount = -1;
|
|
3758
3758
|
}
|
|
3759
3759
|
}
|
|
3760
|
-
const openclawConfigExists =
|
|
3760
|
+
const openclawConfigExists = fs10.existsSync(OPENCLAW_CONFIG_PATH);
|
|
3761
3761
|
if (options.json) {
|
|
3762
3762
|
const data = {
|
|
3763
3763
|
version: VERSION,
|
|
3764
3764
|
initialized: true,
|
|
3765
3765
|
dataDir: SAFECLAW_DIR,
|
|
3766
3766
|
database: dbExists ? "exists" : "not_found",
|
|
3767
|
-
config:
|
|
3767
|
+
config: fs10.existsSync(CONFIG_PATH) ? "exists" : "not_found",
|
|
3768
3768
|
port: config.port,
|
|
3769
3769
|
autoOpenBrowser: config.autoOpenBrowser,
|
|
3770
3770
|
premium: config.premium,
|
|
@@ -3782,7 +3782,7 @@ async function statusCommand(options) {
|
|
|
3782
3782
|
` ${pc4.dim("Database:")} ${dbExists ? pc4.green("exists") : pc4.red("not found")}`
|
|
3783
3783
|
);
|
|
3784
3784
|
console.log(
|
|
3785
|
-
` ${pc4.dim("Config:")} ${
|
|
3785
|
+
` ${pc4.dim("Config:")} ${fs10.existsSync(CONFIG_PATH) ? pc4.green("exists") : pc4.red("not found")}`
|
|
3786
3786
|
);
|
|
3787
3787
|
console.log(` ${pc4.dim("Port:")} ${config.port}`);
|
|
3788
3788
|
console.log(` ${pc4.dim("Auto-open:")} ${config.autoOpenBrowser ? "Yes" : "No"}`);
|
|
@@ -3800,7 +3800,7 @@ async function statusCommand(options) {
|
|
|
3800
3800
|
}
|
|
3801
3801
|
|
|
3802
3802
|
// src/commands/doctor.ts
|
|
3803
|
-
import
|
|
3803
|
+
import fs11 from "fs";
|
|
3804
3804
|
import net from "net";
|
|
3805
3805
|
import Database4 from "better-sqlite3";
|
|
3806
3806
|
import pc5 from "picocolors";
|
|
@@ -3822,7 +3822,7 @@ function checkNodeVersion() {
|
|
|
3822
3822
|
function checkDataDir() {
|
|
3823
3823
|
try {
|
|
3824
3824
|
ensureDataDir();
|
|
3825
|
-
|
|
3825
|
+
fs11.accessSync(SAFECLAW_DIR, fs11.constants.W_OK);
|
|
3826
3826
|
return {
|
|
3827
3827
|
name: "Data directory writable",
|
|
3828
3828
|
status: "pass",
|
|
@@ -3837,7 +3837,7 @@ function checkDataDir() {
|
|
|
3837
3837
|
}
|
|
3838
3838
|
}
|
|
3839
3839
|
function checkDatabase() {
|
|
3840
|
-
if (!
|
|
3840
|
+
if (!fs11.existsSync(DB_PATH)) {
|
|
3841
3841
|
return {
|
|
3842
3842
|
name: "Database",
|
|
3843
3843
|
status: "warn",
|
|
@@ -3880,7 +3880,7 @@ function checkDatabase() {
|
|
|
3880
3880
|
}
|
|
3881
3881
|
}
|
|
3882
3882
|
function checkConfig() {
|
|
3883
|
-
if (!
|
|
3883
|
+
if (!fs11.existsSync(CONFIG_PATH)) {
|
|
3884
3884
|
return {
|
|
3885
3885
|
name: "Config file",
|
|
3886
3886
|
status: "warn",
|
|
@@ -3922,14 +3922,14 @@ async function checkPort() {
|
|
|
3922
3922
|
};
|
|
3923
3923
|
}
|
|
3924
3924
|
function checkOpenClawConfig() {
|
|
3925
|
-
if (!
|
|
3925
|
+
if (!fs11.existsSync(OPENCLAW_DIR)) {
|
|
3926
3926
|
return {
|
|
3927
3927
|
name: "OpenClaw directory",
|
|
3928
3928
|
status: "warn",
|
|
3929
3929
|
message: `${OPENCLAW_DIR} not found`
|
|
3930
3930
|
};
|
|
3931
3931
|
}
|
|
3932
|
-
if (!
|
|
3932
|
+
if (!fs11.existsSync(OPENCLAW_CONFIG_PATH)) {
|
|
3933
3933
|
return {
|
|
3934
3934
|
name: "OpenClaw config",
|
|
3935
3935
|
status: "warn",
|
|
@@ -3961,7 +3961,7 @@ function isPortInUse(port) {
|
|
|
3961
3961
|
});
|
|
3962
3962
|
}
|
|
3963
3963
|
async function checkOpenClawGateway() {
|
|
3964
|
-
if (!
|
|
3964
|
+
if (!fs11.existsSync(OPENCLAW_CONFIG_PATH)) {
|
|
3965
3965
|
return {
|
|
3966
3966
|
name: "OpenClaw gateway",
|
|
3967
3967
|
status: "warn",
|
|
@@ -3969,7 +3969,7 @@ async function checkOpenClawGateway() {
|
|
|
3969
3969
|
};
|
|
3970
3970
|
}
|
|
3971
3971
|
try {
|
|
3972
|
-
const raw = JSON.parse(
|
|
3972
|
+
const raw = JSON.parse(fs11.readFileSync(OPENCLAW_CONFIG_PATH, "utf-8"));
|
|
3973
3973
|
const port = raw?.gateway?.port ?? 18789;
|
|
3974
3974
|
const reachable = await isPortInUse(port);
|
|
3975
3975
|
if (reachable) {
|
|
@@ -3994,10 +3994,10 @@ async function checkOpenClawGateway() {
|
|
|
3994
3994
|
}
|
|
3995
3995
|
function checkLogDir() {
|
|
3996
3996
|
try {
|
|
3997
|
-
if (!
|
|
3998
|
-
|
|
3997
|
+
if (!fs11.existsSync(LOGS_DIR)) {
|
|
3998
|
+
fs11.mkdirSync(LOGS_DIR, { recursive: true });
|
|
3999
3999
|
}
|
|
4000
|
-
|
|
4000
|
+
fs11.accessSync(LOGS_DIR, fs11.constants.W_OK);
|
|
4001
4001
|
return {
|
|
4002
4002
|
name: "Log directory writable",
|
|
4003
4003
|
status: "pass",
|
|
@@ -4139,14 +4139,14 @@ async function configSetCommand(key, value) {
|
|
|
4139
4139
|
}
|
|
4140
4140
|
|
|
4141
4141
|
// src/commands/logs.ts
|
|
4142
|
-
import
|
|
4142
|
+
import fs12 from "fs";
|
|
4143
4143
|
import pc7 from "picocolors";
|
|
4144
4144
|
async function logsCommand(options) {
|
|
4145
4145
|
if (options.clear) {
|
|
4146
4146
|
await clearLogs();
|
|
4147
4147
|
return;
|
|
4148
4148
|
}
|
|
4149
|
-
if (!
|
|
4149
|
+
if (!fs12.existsSync(DEBUG_LOG_PATH)) {
|
|
4150
4150
|
console.log(
|
|
4151
4151
|
pc7.yellow("No log file found.") + " Run " + pc7.cyan("safeclaw start") + " to generate logs."
|
|
4152
4152
|
);
|
|
@@ -4159,7 +4159,7 @@ async function logsCommand(options) {
|
|
|
4159
4159
|
}
|
|
4160
4160
|
}
|
|
4161
4161
|
async function tailLogs(lineCount) {
|
|
4162
|
-
const content =
|
|
4162
|
+
const content = fs12.readFileSync(DEBUG_LOG_PATH, "utf-8");
|
|
4163
4163
|
const lines = content.split("\n").filter(Boolean);
|
|
4164
4164
|
const tail = lines.slice(-lineCount);
|
|
4165
4165
|
if (tail.length === 0) {
|
|
@@ -4177,23 +4177,23 @@ async function tailLogs(lineCount) {
|
|
|
4177
4177
|
async function followLogs() {
|
|
4178
4178
|
console.log(pc7.dim(`Following ${DEBUG_LOG_PATH} (Ctrl+C to stop)
|
|
4179
4179
|
`));
|
|
4180
|
-
if (
|
|
4181
|
-
const content =
|
|
4180
|
+
if (fs12.existsSync(DEBUG_LOG_PATH)) {
|
|
4181
|
+
const content = fs12.readFileSync(DEBUG_LOG_PATH, "utf-8");
|
|
4182
4182
|
const lines = content.split("\n").filter(Boolean);
|
|
4183
4183
|
const tail = lines.slice(-20);
|
|
4184
4184
|
for (const line of tail) {
|
|
4185
4185
|
process.stdout.write(line + "\n");
|
|
4186
4186
|
}
|
|
4187
4187
|
}
|
|
4188
|
-
let position =
|
|
4189
|
-
const watcher =
|
|
4188
|
+
let position = fs12.existsSync(DEBUG_LOG_PATH) ? fs12.statSync(DEBUG_LOG_PATH).size : 0;
|
|
4189
|
+
const watcher = fs12.watch(DEBUG_LOG_PATH, () => {
|
|
4190
4190
|
try {
|
|
4191
|
-
const stat =
|
|
4191
|
+
const stat = fs12.statSync(DEBUG_LOG_PATH);
|
|
4192
4192
|
if (stat.size > position) {
|
|
4193
|
-
const fd =
|
|
4193
|
+
const fd = fs12.openSync(DEBUG_LOG_PATH, "r");
|
|
4194
4194
|
const buffer = Buffer.alloc(stat.size - position);
|
|
4195
|
-
|
|
4196
|
-
|
|
4195
|
+
fs12.readSync(fd, buffer, 0, buffer.length, position);
|
|
4196
|
+
fs12.closeSync(fd);
|
|
4197
4197
|
process.stdout.write(buffer.toString("utf-8"));
|
|
4198
4198
|
position = stat.size;
|
|
4199
4199
|
} else if (stat.size < position) {
|
|
@@ -4211,11 +4211,11 @@ async function followLogs() {
|
|
|
4211
4211
|
});
|
|
4212
4212
|
}
|
|
4213
4213
|
async function clearLogs() {
|
|
4214
|
-
if (!
|
|
4214
|
+
if (!fs12.existsSync(DEBUG_LOG_PATH)) {
|
|
4215
4215
|
console.log(pc7.dim("No log file to clear."));
|
|
4216
4216
|
return;
|
|
4217
4217
|
}
|
|
4218
|
-
|
|
4218
|
+
fs12.writeFileSync(DEBUG_LOG_PATH, "");
|
|
4219
4219
|
console.log(pc7.green("Log file cleared."));
|
|
4220
4220
|
}
|
|
4221
4221
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "safeclaw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Security management dashboard for AI agents – intercept, monitor, and control what AI agents can do on your system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -24,16 +24,6 @@
|
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=20.0.0"
|
|
26
26
|
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build:web": "pnpm --filter @safeclaw/web build && cp -r ../web/dist/* ./public/",
|
|
29
|
-
"build": "tsup",
|
|
30
|
-
"dev": "tsx watch src/main.ts start",
|
|
31
|
-
"typecheck": "tsc --noEmit",
|
|
32
|
-
"db:push": "drizzle-kit push",
|
|
33
|
-
"db:studio": "drizzle-kit studio",
|
|
34
|
-
"test": "vitest run",
|
|
35
|
-
"test:watch": "vitest"
|
|
36
|
-
},
|
|
37
27
|
"dependencies": {
|
|
38
28
|
"@fastify/cors": "^10.0.0",
|
|
39
29
|
"@fastify/static": "^8.0.0",
|
|
@@ -50,7 +40,6 @@
|
|
|
50
40
|
"zod": "^3.24.0"
|
|
51
41
|
},
|
|
52
42
|
"devDependencies": {
|
|
53
|
-
"@safeclaw/shared": "workspace:*",
|
|
54
43
|
"@types/better-sqlite3": "^7.6.0",
|
|
55
44
|
"@types/node": "^22.0.0",
|
|
56
45
|
"@types/ws": "^8.18.1",
|
|
@@ -58,12 +47,23 @@
|
|
|
58
47
|
"tsup": "^8.5.1",
|
|
59
48
|
"tsx": "^4.19.0",
|
|
60
49
|
"typescript": "^5.7.0",
|
|
61
|
-
"vitest": "^4.0.18"
|
|
50
|
+
"vitest": "^4.0.18",
|
|
51
|
+
"@safeclaw/shared": "0.1.0"
|
|
62
52
|
},
|
|
63
53
|
"files": [
|
|
64
54
|
"dist/main.js",
|
|
65
55
|
"bin",
|
|
66
56
|
"public",
|
|
67
57
|
"!public/.gitkeep"
|
|
68
|
-
]
|
|
69
|
-
|
|
58
|
+
],
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build:web": "pnpm --filter @safeclaw/web build && cp -r ../web/dist/* ./public/",
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"dev": "tsx watch src/main.ts start",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"db:push": "drizzle-kit push",
|
|
65
|
+
"db:studio": "drizzle-kit studio",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"test:watch": "vitest"
|
|
68
|
+
}
|
|
69
|
+
}
|