@agenshield/daemon 0.4.2 → 0.4.4
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/index.js +174 -89
- package/main.js +174 -89
- package/package.json +4 -4
- package/routes/activity.d.ts +6 -0
- package/routes/activity.d.ts.map +1 -0
- package/routes/index.d.ts.map +1 -1
- package/routes/marketplace.d.ts.map +1 -1
- package/routes/skills.d.ts.map +1 -1
- package/services/activity-log.d.ts +4 -0
- package/services/activity-log.d.ts.map +1 -1
- package/services/openclaw-config.d.ts.map +1 -1
- package/ui-assets/assets/{index-Chp3YFDr.js → index-gZFEJ5lI.js} +105 -105
- package/ui-assets/index.html +1 -1
package/index.js
CHANGED
|
@@ -4986,7 +4986,7 @@ async function agencoRoutes(app) {
|
|
|
4986
4986
|
// libs/shield-daemon/src/routes/skills.ts
|
|
4987
4987
|
import * as fs13 from "node:fs";
|
|
4988
4988
|
import * as path12 from "node:path";
|
|
4989
|
-
import { execSync as
|
|
4989
|
+
import { execSync as execSync8 } from "node:child_process";
|
|
4990
4990
|
import { parseSkillMd } from "@agenshield/sandbox";
|
|
4991
4991
|
|
|
4992
4992
|
// libs/shield-daemon/src/services/skill-analyzer.ts
|
|
@@ -5269,6 +5269,7 @@ function removeSkillPolicy(name) {
|
|
|
5269
5269
|
// libs/shield-daemon/src/services/openclaw-config.ts
|
|
5270
5270
|
import * as fs11 from "node:fs";
|
|
5271
5271
|
import * as path10 from "node:path";
|
|
5272
|
+
import { execSync as execSync7 } from "node:child_process";
|
|
5272
5273
|
function getOpenClawConfigPath() {
|
|
5273
5274
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5274
5275
|
return path10.join(agentHome, ".openclaw", "openclaw.json");
|
|
@@ -5286,7 +5287,16 @@ function readConfig() {
|
|
|
5286
5287
|
}
|
|
5287
5288
|
function writeConfig(config) {
|
|
5288
5289
|
const configPath = getOpenClawConfigPath();
|
|
5290
|
+
fs11.mkdirSync(path10.dirname(configPath), { recursive: true });
|
|
5289
5291
|
fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5292
|
+
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5293
|
+
const brokerUser = path10.basename(agentHome) + "_broker";
|
|
5294
|
+
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5295
|
+
try {
|
|
5296
|
+
execSync7(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
|
|
5297
|
+
execSync7(`chmod 664 "${configPath}"`, { stdio: "pipe" });
|
|
5298
|
+
} catch {
|
|
5299
|
+
}
|
|
5290
5300
|
}
|
|
5291
5301
|
function addSkillEntry(slug, env) {
|
|
5292
5302
|
const config = readConfig();
|
|
@@ -5740,23 +5750,45 @@ async function getCachedAnalysis2(skillName, publisher) {
|
|
|
5740
5750
|
}
|
|
5741
5751
|
|
|
5742
5752
|
// libs/shield-daemon/src/routes/skills.ts
|
|
5753
|
+
function readSkillDescription(skillDir) {
|
|
5754
|
+
try {
|
|
5755
|
+
const skillMdPath = path12.join(skillDir, "SKILL.md");
|
|
5756
|
+
if (!fs13.existsSync(skillMdPath)) return void 0;
|
|
5757
|
+
const content = fs13.readFileSync(skillMdPath, "utf-8");
|
|
5758
|
+
const parsed = parseSkillMd(content);
|
|
5759
|
+
return parsed?.metadata?.description ?? void 0;
|
|
5760
|
+
} catch {
|
|
5761
|
+
return void 0;
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5743
5764
|
async function skillsRoutes(app) {
|
|
5744
5765
|
app.get("/skills", async (_request, reply) => {
|
|
5745
5766
|
const approved = listApproved();
|
|
5746
5767
|
const quarantined = listQuarantined();
|
|
5747
5768
|
const downloaded = listDownloadedSkills();
|
|
5769
|
+
const skillsDir2 = getSkillsDir();
|
|
5748
5770
|
const approvedNames = new Set(approved.map((a) => a.name));
|
|
5749
5771
|
const availableDownloads = downloaded.filter((d) => !approvedNames.has(d.slug));
|
|
5750
|
-
const
|
|
5772
|
+
const quarantinedNames = new Set(quarantined.map((q) => q.name));
|
|
5773
|
+
let onDiskNames = [];
|
|
5774
|
+
if (skillsDir2) {
|
|
5775
|
+
try {
|
|
5776
|
+
onDiskNames = fs13.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
5777
|
+
} catch {
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
5780
|
+
const workspaceNames = onDiskNames.filter(
|
|
5781
|
+
(n) => !approvedNames.has(n) && !quarantinedNames.has(n)
|
|
5782
|
+
);
|
|
5751
5783
|
const data = [
|
|
5752
|
-
// Approved → active
|
|
5784
|
+
// Approved → active (with descriptions from SKILL.md)
|
|
5753
5785
|
...approved.map((a) => ({
|
|
5754
5786
|
name: a.name,
|
|
5755
5787
|
source: "user",
|
|
5756
5788
|
status: "active",
|
|
5757
5789
|
path: path12.join(skillsDir2 ?? "", a.name),
|
|
5758
5790
|
publisher: a.publisher,
|
|
5759
|
-
description: void 0
|
|
5791
|
+
description: skillsDir2 ? readSkillDescription(path12.join(skillsDir2, a.name)) : void 0
|
|
5760
5792
|
})),
|
|
5761
5793
|
// Quarantined
|
|
5762
5794
|
...quarantined.map((q) => ({
|
|
@@ -5766,6 +5798,14 @@ async function skillsRoutes(app) {
|
|
|
5766
5798
|
path: q.originalPath,
|
|
5767
5799
|
description: void 0
|
|
5768
5800
|
})),
|
|
5801
|
+
// Workspace: on disk but not approved or quarantined
|
|
5802
|
+
...workspaceNames.map((name) => ({
|
|
5803
|
+
name,
|
|
5804
|
+
source: "workspace",
|
|
5805
|
+
status: "workspace",
|
|
5806
|
+
path: path12.join(skillsDir2 ?? "", name),
|
|
5807
|
+
description: skillsDir2 ? readSkillDescription(path12.join(skillsDir2, name)) : void 0
|
|
5808
|
+
})),
|
|
5769
5809
|
// Downloaded (not installed) → available
|
|
5770
5810
|
...availableDownloads.map((d) => ({
|
|
5771
5811
|
name: d.slug,
|
|
@@ -5916,8 +5956,8 @@ async function skillsRoutes(app) {
|
|
|
5916
5956
|
fs13.writeFileSync(filePath, file.content, "utf-8");
|
|
5917
5957
|
}
|
|
5918
5958
|
try {
|
|
5919
|
-
|
|
5920
|
-
|
|
5959
|
+
execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
5960
|
+
execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
|
|
5921
5961
|
} catch {
|
|
5922
5962
|
}
|
|
5923
5963
|
createSkillWrapper(name, binDir);
|
|
@@ -5978,8 +6018,8 @@ async function skillsRoutes(app) {
|
|
|
5978
6018
|
fs13.writeFileSync(filePath, file.content, "utf-8");
|
|
5979
6019
|
}
|
|
5980
6020
|
try {
|
|
5981
|
-
|
|
5982
|
-
|
|
6021
|
+
execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
6022
|
+
execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
|
|
5983
6023
|
} catch {
|
|
5984
6024
|
}
|
|
5985
6025
|
createSkillWrapper(name, binDir);
|
|
@@ -7015,8 +7055,6 @@ async function marketplaceRoutes(app) {
|
|
|
7015
7055
|
if (brokerResult.wrapperPath) {
|
|
7016
7056
|
logs.push(`Wrapper created: ${brokerResult.wrapperPath}`);
|
|
7017
7057
|
}
|
|
7018
|
-
addSkillEntry(slug);
|
|
7019
|
-
logs.push("Config entry added");
|
|
7020
7058
|
addSkillPolicy(slug);
|
|
7021
7059
|
logs.push("Policy rule added");
|
|
7022
7060
|
emitSkillInstallProgress(slug, "local_analysis", "Running local analysis");
|
|
@@ -7091,6 +7129,120 @@ async function fsRoutes(app) {
|
|
|
7091
7129
|
});
|
|
7092
7130
|
}
|
|
7093
7131
|
|
|
7132
|
+
// libs/shield-daemon/src/services/activity-log.ts
|
|
7133
|
+
import * as fs17 from "node:fs";
|
|
7134
|
+
import * as path16 from "node:path";
|
|
7135
|
+
var ACTIVITY_FILE = "activity.jsonl";
|
|
7136
|
+
var MAX_SIZE_BYTES = 100 * 1024 * 1024;
|
|
7137
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
7138
|
+
var PRUNE_INTERVAL = 1e3;
|
|
7139
|
+
var instance = null;
|
|
7140
|
+
function getActivityLog() {
|
|
7141
|
+
if (!instance) {
|
|
7142
|
+
instance = new ActivityLog();
|
|
7143
|
+
}
|
|
7144
|
+
return instance;
|
|
7145
|
+
}
|
|
7146
|
+
var ActivityLog = class {
|
|
7147
|
+
filePath;
|
|
7148
|
+
writeCount = 0;
|
|
7149
|
+
unsubscribe;
|
|
7150
|
+
constructor() {
|
|
7151
|
+
this.filePath = path16.join(getConfigDir(), ACTIVITY_FILE);
|
|
7152
|
+
}
|
|
7153
|
+
/** Read historical events from the JSONL file, newest first */
|
|
7154
|
+
getHistory(limit = 500) {
|
|
7155
|
+
if (!fs17.existsSync(this.filePath)) return [];
|
|
7156
|
+
const content = fs17.readFileSync(this.filePath, "utf-8");
|
|
7157
|
+
const lines = content.split("\n").filter(Boolean);
|
|
7158
|
+
const events = [];
|
|
7159
|
+
for (const line of lines) {
|
|
7160
|
+
try {
|
|
7161
|
+
const evt = JSON.parse(line);
|
|
7162
|
+
if (evt.type === "heartbeat") continue;
|
|
7163
|
+
events.push(evt);
|
|
7164
|
+
} catch {
|
|
7165
|
+
}
|
|
7166
|
+
}
|
|
7167
|
+
events.reverse();
|
|
7168
|
+
return events.slice(0, limit);
|
|
7169
|
+
}
|
|
7170
|
+
start() {
|
|
7171
|
+
this.pruneOldEntries();
|
|
7172
|
+
this.unsubscribe = daemonEvents.subscribe((event) => {
|
|
7173
|
+
this.append(event);
|
|
7174
|
+
});
|
|
7175
|
+
}
|
|
7176
|
+
stop() {
|
|
7177
|
+
this.unsubscribe?.();
|
|
7178
|
+
}
|
|
7179
|
+
append(event) {
|
|
7180
|
+
const line = JSON.stringify(event) + "\n";
|
|
7181
|
+
fs17.appendFileSync(this.filePath, line, "utf-8");
|
|
7182
|
+
this.writeCount++;
|
|
7183
|
+
if (this.writeCount % PRUNE_INTERVAL === 0) {
|
|
7184
|
+
this.rotate();
|
|
7185
|
+
}
|
|
7186
|
+
}
|
|
7187
|
+
rotate() {
|
|
7188
|
+
try {
|
|
7189
|
+
const stat = fs17.statSync(this.filePath);
|
|
7190
|
+
if (stat.size > MAX_SIZE_BYTES) {
|
|
7191
|
+
this.truncateBySize();
|
|
7192
|
+
}
|
|
7193
|
+
} catch {
|
|
7194
|
+
}
|
|
7195
|
+
this.pruneOldEntries();
|
|
7196
|
+
}
|
|
7197
|
+
/** Keep newest half of lines when file exceeds size limit */
|
|
7198
|
+
truncateBySize() {
|
|
7199
|
+
const content = fs17.readFileSync(this.filePath, "utf-8");
|
|
7200
|
+
const lines = content.split("\n").filter(Boolean);
|
|
7201
|
+
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
7202
|
+
fs17.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
|
|
7203
|
+
}
|
|
7204
|
+
/** Remove entries older than 24 hours */
|
|
7205
|
+
pruneOldEntries() {
|
|
7206
|
+
if (!fs17.existsSync(this.filePath)) return;
|
|
7207
|
+
const content = fs17.readFileSync(this.filePath, "utf-8");
|
|
7208
|
+
const lines = content.split("\n").filter(Boolean);
|
|
7209
|
+
const cutoff = Date.now() - MAX_AGE_MS;
|
|
7210
|
+
const kept = lines.filter((line) => {
|
|
7211
|
+
try {
|
|
7212
|
+
const evt = JSON.parse(line);
|
|
7213
|
+
return new Date(evt.timestamp).getTime() >= cutoff;
|
|
7214
|
+
} catch {
|
|
7215
|
+
return false;
|
|
7216
|
+
}
|
|
7217
|
+
});
|
|
7218
|
+
if (kept.length < lines.length) {
|
|
7219
|
+
fs17.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
};
|
|
7223
|
+
|
|
7224
|
+
// libs/shield-daemon/src/routes/activity.ts
|
|
7225
|
+
async function activityRoutes(app) {
|
|
7226
|
+
app.get(
|
|
7227
|
+
"/activity",
|
|
7228
|
+
async (request) => {
|
|
7229
|
+
const authenticated = isAuthenticated(request);
|
|
7230
|
+
const raw = Number(request.query.limit) || 500;
|
|
7231
|
+
const limit = Math.min(Math.max(raw, 1), 1e4);
|
|
7232
|
+
const events = getActivityLog().getHistory(limit);
|
|
7233
|
+
if (authenticated) {
|
|
7234
|
+
return { data: events };
|
|
7235
|
+
}
|
|
7236
|
+
const stripped = events.map((e) => ({
|
|
7237
|
+
type: e.type,
|
|
7238
|
+
timestamp: e.timestamp,
|
|
7239
|
+
data: {}
|
|
7240
|
+
}));
|
|
7241
|
+
return { data: stripped };
|
|
7242
|
+
}
|
|
7243
|
+
);
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7094
7246
|
// libs/shield-daemon/src/routes/rpc.ts
|
|
7095
7247
|
function globToRegex(pattern) {
|
|
7096
7248
|
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{GLOBSTAR}}/g, ".*");
|
|
@@ -7289,7 +7441,7 @@ async function registerRoutes(app) {
|
|
|
7289
7441
|
});
|
|
7290
7442
|
app.addHook("onResponse", (request, reply, done) => {
|
|
7291
7443
|
if (!request.url.startsWith("/sse") && !request.url.startsWith("/rpc") && !request.url.includes(".") && !request.url.endsWith("/health")) {
|
|
7292
|
-
if (request.method === "GET" && request.url.startsWith("/api/status") && reply.statusCode === 200) {
|
|
7444
|
+
if (request.method === "GET" && (request.url.startsWith("/api/status") || request.url.startsWith("/api/activity")) && reply.statusCode === 200) {
|
|
7293
7445
|
done();
|
|
7294
7446
|
return;
|
|
7295
7447
|
}
|
|
@@ -7342,28 +7494,29 @@ async function registerRoutes(app) {
|
|
|
7342
7494
|
await api.register(secretsRoutes);
|
|
7343
7495
|
await api.register(marketplaceRoutes);
|
|
7344
7496
|
await api.register(fsRoutes);
|
|
7497
|
+
await api.register(activityRoutes);
|
|
7345
7498
|
},
|
|
7346
7499
|
{ prefix: API_PREFIX }
|
|
7347
7500
|
);
|
|
7348
7501
|
}
|
|
7349
7502
|
|
|
7350
7503
|
// libs/shield-daemon/src/static.ts
|
|
7351
|
-
import * as
|
|
7352
|
-
import * as
|
|
7504
|
+
import * as fs18 from "node:fs";
|
|
7505
|
+
import * as path17 from "node:path";
|
|
7353
7506
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7354
7507
|
var __filename = fileURLToPath2(import.meta.url);
|
|
7355
|
-
var __dirname =
|
|
7508
|
+
var __dirname = path17.dirname(__filename);
|
|
7356
7509
|
function getUiAssetsPath() {
|
|
7357
|
-
const pkgRootPath =
|
|
7358
|
-
if (
|
|
7510
|
+
const pkgRootPath = path17.join(__dirname, "..", "ui-assets");
|
|
7511
|
+
if (fs18.existsSync(pkgRootPath)) {
|
|
7359
7512
|
return pkgRootPath;
|
|
7360
7513
|
}
|
|
7361
|
-
const bundledPath =
|
|
7362
|
-
if (
|
|
7514
|
+
const bundledPath = path17.join(__dirname, "ui-assets");
|
|
7515
|
+
if (fs18.existsSync(bundledPath)) {
|
|
7363
7516
|
return bundledPath;
|
|
7364
7517
|
}
|
|
7365
|
-
const devPath =
|
|
7366
|
-
if (
|
|
7518
|
+
const devPath = path17.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
|
|
7519
|
+
if (fs18.existsSync(devPath)) {
|
|
7367
7520
|
return devPath;
|
|
7368
7521
|
}
|
|
7369
7522
|
return null;
|
|
@@ -7422,74 +7575,6 @@ function stopSecurityWatcher() {
|
|
|
7422
7575
|
}
|
|
7423
7576
|
}
|
|
7424
7577
|
|
|
7425
|
-
// libs/shield-daemon/src/services/activity-log.ts
|
|
7426
|
-
import * as fs18 from "node:fs";
|
|
7427
|
-
import * as path17 from "node:path";
|
|
7428
|
-
var ACTIVITY_FILE = "activity.jsonl";
|
|
7429
|
-
var MAX_SIZE_BYTES = 100 * 1024 * 1024;
|
|
7430
|
-
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
7431
|
-
var PRUNE_INTERVAL = 1e3;
|
|
7432
|
-
var ActivityLog = class {
|
|
7433
|
-
filePath;
|
|
7434
|
-
writeCount = 0;
|
|
7435
|
-
unsubscribe;
|
|
7436
|
-
constructor() {
|
|
7437
|
-
this.filePath = path17.join(getConfigDir(), ACTIVITY_FILE);
|
|
7438
|
-
}
|
|
7439
|
-
start() {
|
|
7440
|
-
this.pruneOldEntries();
|
|
7441
|
-
this.unsubscribe = daemonEvents.subscribe((event) => {
|
|
7442
|
-
this.append(event);
|
|
7443
|
-
});
|
|
7444
|
-
}
|
|
7445
|
-
stop() {
|
|
7446
|
-
this.unsubscribe?.();
|
|
7447
|
-
}
|
|
7448
|
-
append(event) {
|
|
7449
|
-
const line = JSON.stringify(event) + "\n";
|
|
7450
|
-
fs18.appendFileSync(this.filePath, line, "utf-8");
|
|
7451
|
-
this.writeCount++;
|
|
7452
|
-
if (this.writeCount % PRUNE_INTERVAL === 0) {
|
|
7453
|
-
this.rotate();
|
|
7454
|
-
}
|
|
7455
|
-
}
|
|
7456
|
-
rotate() {
|
|
7457
|
-
try {
|
|
7458
|
-
const stat = fs18.statSync(this.filePath);
|
|
7459
|
-
if (stat.size > MAX_SIZE_BYTES) {
|
|
7460
|
-
this.truncateBySize();
|
|
7461
|
-
}
|
|
7462
|
-
} catch {
|
|
7463
|
-
}
|
|
7464
|
-
this.pruneOldEntries();
|
|
7465
|
-
}
|
|
7466
|
-
/** Keep newest half of lines when file exceeds size limit */
|
|
7467
|
-
truncateBySize() {
|
|
7468
|
-
const content = fs18.readFileSync(this.filePath, "utf-8");
|
|
7469
|
-
const lines = content.split("\n").filter(Boolean);
|
|
7470
|
-
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
7471
|
-
fs18.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
|
|
7472
|
-
}
|
|
7473
|
-
/** Remove entries older than 24 hours */
|
|
7474
|
-
pruneOldEntries() {
|
|
7475
|
-
if (!fs18.existsSync(this.filePath)) return;
|
|
7476
|
-
const content = fs18.readFileSync(this.filePath, "utf-8");
|
|
7477
|
-
const lines = content.split("\n").filter(Boolean);
|
|
7478
|
-
const cutoff = Date.now() - MAX_AGE_MS;
|
|
7479
|
-
const kept = lines.filter((line) => {
|
|
7480
|
-
try {
|
|
7481
|
-
const evt = JSON.parse(line);
|
|
7482
|
-
return new Date(evt.timestamp).getTime() >= cutoff;
|
|
7483
|
-
} catch {
|
|
7484
|
-
return false;
|
|
7485
|
-
}
|
|
7486
|
-
});
|
|
7487
|
-
if (kept.length < lines.length) {
|
|
7488
|
-
fs18.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
|
|
7489
|
-
}
|
|
7490
|
-
}
|
|
7491
|
-
};
|
|
7492
|
-
|
|
7493
7578
|
// libs/shield-daemon/src/server.ts
|
|
7494
7579
|
async function createServer(config) {
|
|
7495
7580
|
const app = Fastify({
|
|
@@ -7528,7 +7613,7 @@ async function startServer(config) {
|
|
|
7528
7613
|
onQuarantined: (info) => emitSkillQuarantined(info.name, info.reason),
|
|
7529
7614
|
onApproved: (name) => emitSkillApproved(name)
|
|
7530
7615
|
}, 3e4);
|
|
7531
|
-
const activityLog =
|
|
7616
|
+
const activityLog = getActivityLog();
|
|
7532
7617
|
activityLog.start();
|
|
7533
7618
|
try {
|
|
7534
7619
|
const vault = getVault();
|