@agenshield/daemon 0.7.1 → 0.7.2
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 +319 -60
- package/main.js +319 -60
- package/package.json +5 -5
- package/proxy/pool.d.ts.map +1 -1
- package/proxy/server.d.ts +1 -1
- package/proxy/server.d.ts.map +1 -1
- package/routes/marketplace.d.ts.map +1 -1
- package/routes/rpc.d.ts.map +1 -1
- package/routes/skills.d.ts.map +1 -1
- package/routes/sse.d.ts.map +1 -1
- package/server.d.ts.map +1 -1
- package/services/marketplace.d.ts +9 -0
- package/services/marketplace.d.ts.map +1 -1
- package/services/skill-deps.d.ts.map +1 -1
- package/services/skill-tag-injector.d.ts +26 -0
- package/services/skill-tag-injector.d.ts.map +1 -0
- package/ui-assets/assets/{index-DJILlJ1g.js → index-xi3PdcyO.js} +167 -167
- package/ui-assets/index.html +1 -1
- package/vault/index.d.ts +1 -0
- package/vault/index.d.ts.map +1 -1
- package/vault/installation-key.d.ts +30 -0
- package/vault/installation-key.d.ts.map +1 -0
- package/watchers/skills.d.ts +3 -1
- package/watchers/skills.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -1295,6 +1295,41 @@ var init_crypto = __esm({
|
|
|
1295
1295
|
}
|
|
1296
1296
|
});
|
|
1297
1297
|
|
|
1298
|
+
// libs/shield-daemon/src/vault/installation-key.ts
|
|
1299
|
+
import * as crypto2 from "node:crypto";
|
|
1300
|
+
async function getInstallationKey() {
|
|
1301
|
+
if (cachedKey) return cachedKey;
|
|
1302
|
+
const vault = getVault();
|
|
1303
|
+
const contents = await vault.load();
|
|
1304
|
+
if (contents.installationKey) {
|
|
1305
|
+
cachedKey = contents.installationKey;
|
|
1306
|
+
return cachedKey;
|
|
1307
|
+
}
|
|
1308
|
+
const key = crypto2.randomBytes(32).toString("hex");
|
|
1309
|
+
await vault.set("installationKey", key);
|
|
1310
|
+
cachedKey = key;
|
|
1311
|
+
console.log("[InstallationKey] Generated new installation key");
|
|
1312
|
+
return key;
|
|
1313
|
+
}
|
|
1314
|
+
async function getInstallationTag() {
|
|
1315
|
+
const key = await getInstallationKey();
|
|
1316
|
+
return `${INSTALLATION_KEY_PREFIX}${key}`;
|
|
1317
|
+
}
|
|
1318
|
+
function hasValidInstallationTagSync(tags) {
|
|
1319
|
+
if (!cachedKey) return false;
|
|
1320
|
+
const fullTag = `${INSTALLATION_KEY_PREFIX}${cachedKey}`;
|
|
1321
|
+
return tags.some((tag) => tag === fullTag);
|
|
1322
|
+
}
|
|
1323
|
+
var INSTALLATION_KEY_PREFIX, cachedKey;
|
|
1324
|
+
var init_installation_key = __esm({
|
|
1325
|
+
"libs/shield-daemon/src/vault/installation-key.ts"() {
|
|
1326
|
+
"use strict";
|
|
1327
|
+
init_vault();
|
|
1328
|
+
INSTALLATION_KEY_PREFIX = "agenshield-";
|
|
1329
|
+
cachedKey = null;
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1298
1333
|
// libs/shield-daemon/src/vault/index.ts
|
|
1299
1334
|
import * as fs4 from "node:fs";
|
|
1300
1335
|
import * as path4 from "node:path";
|
|
@@ -1315,6 +1350,7 @@ var init_vault = __esm({
|
|
|
1315
1350
|
init_crypto();
|
|
1316
1351
|
init_paths();
|
|
1317
1352
|
init_crypto();
|
|
1353
|
+
init_installation_key();
|
|
1318
1354
|
Vault = class {
|
|
1319
1355
|
key;
|
|
1320
1356
|
vaultPath;
|
|
@@ -1849,7 +1885,7 @@ init_state();
|
|
|
1849
1885
|
init_vault();
|
|
1850
1886
|
|
|
1851
1887
|
// libs/shield-daemon/src/auth/session.ts
|
|
1852
|
-
import * as
|
|
1888
|
+
import * as crypto3 from "node:crypto";
|
|
1853
1889
|
import { DEFAULT_AUTH_CONFIG } from "@agenshield/ipc";
|
|
1854
1890
|
var SessionManager = class {
|
|
1855
1891
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -1863,7 +1899,7 @@ var SessionManager = class {
|
|
|
1863
1899
|
* Generate a secure random token
|
|
1864
1900
|
*/
|
|
1865
1901
|
generateToken() {
|
|
1866
|
-
return
|
|
1902
|
+
return crypto3.randomBytes(32).toString("base64url");
|
|
1867
1903
|
}
|
|
1868
1904
|
/**
|
|
1869
1905
|
* Create a new session
|
|
@@ -2440,7 +2476,7 @@ function generatePolicyMarkdown(policies, knownSkills) {
|
|
|
2440
2476
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
2441
2477
|
import * as fs11 from "node:fs";
|
|
2442
2478
|
import * as path11 from "node:path";
|
|
2443
|
-
import * as
|
|
2479
|
+
import * as crypto4 from "node:crypto";
|
|
2444
2480
|
import { parseSkillMd } from "@agenshield/sandbox";
|
|
2445
2481
|
|
|
2446
2482
|
// libs/shield-daemon/src/services/marketplace.ts
|
|
@@ -2629,6 +2665,17 @@ function updateDownloadedAnalysis(slug, analysis) {
|
|
|
2629
2665
|
} catch {
|
|
2630
2666
|
}
|
|
2631
2667
|
}
|
|
2668
|
+
function markDownloadedAsInstalled(slug) {
|
|
2669
|
+
const metaPath = path9.join(getMarketplaceDir(), slug, "metadata.json");
|
|
2670
|
+
try {
|
|
2671
|
+
if (!fs9.existsSync(metaPath)) return;
|
|
2672
|
+
const meta = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
|
|
2673
|
+
if (meta.wasInstalled) return;
|
|
2674
|
+
meta.wasInstalled = true;
|
|
2675
|
+
fs9.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
2676
|
+
} catch {
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2632
2679
|
function listDownloadedSkills() {
|
|
2633
2680
|
const baseDir = getMarketplaceDir();
|
|
2634
2681
|
if (!fs9.existsSync(baseDir)) return [];
|
|
@@ -2649,7 +2696,8 @@ function listDownloadedSkills() {
|
|
|
2649
2696
|
tags: meta.tags ?? [],
|
|
2650
2697
|
hasAnalysis: !!meta.analysis,
|
|
2651
2698
|
source: meta.source,
|
|
2652
|
-
analysis: meta.analysis
|
|
2699
|
+
analysis: meta.analysis,
|
|
2700
|
+
wasInstalled: meta.wasInstalled ?? false
|
|
2653
2701
|
});
|
|
2654
2702
|
} catch {
|
|
2655
2703
|
}
|
|
@@ -3246,6 +3294,9 @@ function emitSkillUntrustedDetected(name, reason) {
|
|
|
3246
3294
|
function emitSkillApproved(skillName) {
|
|
3247
3295
|
daemonEvents.broadcast("skills:approved", { name: skillName });
|
|
3248
3296
|
}
|
|
3297
|
+
function emitExecDenied(command, reason) {
|
|
3298
|
+
daemonEvents.broadcast("exec:denied", { command, reason });
|
|
3299
|
+
}
|
|
3249
3300
|
function emitAgenCoAuthRequired(authUrl, integration) {
|
|
3250
3301
|
daemonEvents.broadcast("agenco:auth_required", { authUrl, integration });
|
|
3251
3302
|
}
|
|
@@ -3285,6 +3336,56 @@ function emitEvent(type, data) {
|
|
|
3285
3336
|
|
|
3286
3337
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
3287
3338
|
init_paths();
|
|
3339
|
+
|
|
3340
|
+
// libs/shield-daemon/src/services/skill-tag-injector.ts
|
|
3341
|
+
init_installation_key();
|
|
3342
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
3343
|
+
var FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
3344
|
+
async function injectInstallationTag(content) {
|
|
3345
|
+
const tag = await getInstallationTag();
|
|
3346
|
+
const match = content.match(FRONTMATTER_RE);
|
|
3347
|
+
if (!match) {
|
|
3348
|
+
return `---
|
|
3349
|
+
tags:
|
|
3350
|
+
- ${tag}
|
|
3351
|
+
---
|
|
3352
|
+
${content}`;
|
|
3353
|
+
}
|
|
3354
|
+
try {
|
|
3355
|
+
const metadata = parseYaml(match[1]);
|
|
3356
|
+
if (!metadata || typeof metadata !== "object") {
|
|
3357
|
+
return content;
|
|
3358
|
+
}
|
|
3359
|
+
if (!Array.isArray(metadata.tags)) {
|
|
3360
|
+
metadata.tags = [];
|
|
3361
|
+
}
|
|
3362
|
+
metadata.tags = metadata.tags.filter(
|
|
3363
|
+
(t) => typeof t !== "string" || !t.startsWith("agenshield-")
|
|
3364
|
+
);
|
|
3365
|
+
metadata.tags.push(tag);
|
|
3366
|
+
return `---
|
|
3367
|
+
${stringifyYaml(metadata).trimEnd()}
|
|
3368
|
+
---
|
|
3369
|
+
${match[2]}`;
|
|
3370
|
+
} catch {
|
|
3371
|
+
return content;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
function extractTagsFromSkillMd(content) {
|
|
3375
|
+
const match = content.match(FRONTMATTER_RE);
|
|
3376
|
+
if (!match) return [];
|
|
3377
|
+
try {
|
|
3378
|
+
const metadata = parseYaml(match[1]);
|
|
3379
|
+
if (!metadata || typeof metadata !== "object") return [];
|
|
3380
|
+
if (!Array.isArray(metadata.tags)) return [];
|
|
3381
|
+
return metadata.tags.filter((t) => typeof t === "string");
|
|
3382
|
+
} catch {
|
|
3383
|
+
return [];
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// libs/shield-daemon/src/watchers/skills.ts
|
|
3388
|
+
init_installation_key();
|
|
3288
3389
|
function getApprovedSkillsPath() {
|
|
3289
3390
|
return path11.join(getSystemConfigDir(), "approved-skills.json");
|
|
3290
3391
|
}
|
|
@@ -3423,7 +3524,7 @@ function computeSkillHash(skillDir) {
|
|
|
3423
3524
|
const files = readSkillFiles(skillDir);
|
|
3424
3525
|
if (files.length === 0) return null;
|
|
3425
3526
|
files.sort((a, b) => a.name.localeCompare(b.name));
|
|
3426
|
-
const hash =
|
|
3527
|
+
const hash = crypto4.createHash("sha256");
|
|
3427
3528
|
for (const file of files) {
|
|
3428
3529
|
hash.update(file.name);
|
|
3429
3530
|
hash.update(file.content);
|
|
@@ -3452,10 +3553,33 @@ function scanSkills() {
|
|
|
3452
3553
|
const approvedEntry = approvedMap.get(skillName);
|
|
3453
3554
|
if (!approvedEntry) {
|
|
3454
3555
|
const fullPath = path11.join(skillsDir, skillName);
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3556
|
+
let autoApproved = false;
|
|
3557
|
+
for (const mdName of ["SKILL.md", "skill.md"]) {
|
|
3558
|
+
const mdPath = path11.join(fullPath, mdName);
|
|
3559
|
+
try {
|
|
3560
|
+
if (fs11.existsSync(mdPath)) {
|
|
3561
|
+
const content = fs11.readFileSync(mdPath, "utf-8");
|
|
3562
|
+
const tags = extractTagsFromSkillMd(content);
|
|
3563
|
+
if (hasValidInstallationTagSync(tags)) {
|
|
3564
|
+
console.log(`[SkillsWatcher] Auto-approving skill with valid installation tag: ${skillName}`);
|
|
3565
|
+
const hash = computeSkillHash(fullPath);
|
|
3566
|
+
addToApprovedList(skillName, void 0, hash ?? void 0);
|
|
3567
|
+
if (callbacks.onApproved) {
|
|
3568
|
+
callbacks.onApproved(skillName);
|
|
3569
|
+
}
|
|
3570
|
+
autoApproved = true;
|
|
3571
|
+
break;
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
} catch {
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
if (!autoApproved) {
|
|
3578
|
+
const slug = moveToMarketplace(skillName, fullPath);
|
|
3579
|
+
if (slug) {
|
|
3580
|
+
if (callbacks.onUntrustedDetected) {
|
|
3581
|
+
callbacks.onUntrustedDetected({ name: skillName, reason: "Skill not in approved list" });
|
|
3582
|
+
}
|
|
3459
3583
|
}
|
|
3460
3584
|
}
|
|
3461
3585
|
} else if (approvedEntry.hash) {
|
|
@@ -3561,7 +3685,7 @@ function approveSkill(skillName) {
|
|
|
3561
3685
|
const cachedFiles = getDownloadedSkillFiles(slug);
|
|
3562
3686
|
let hash;
|
|
3563
3687
|
if (cachedFiles.length > 0) {
|
|
3564
|
-
const h =
|
|
3688
|
+
const h = crypto4.createHash("sha256");
|
|
3565
3689
|
const sorted = [...cachedFiles].sort((a, b) => a.name.localeCompare(b.name));
|
|
3566
3690
|
for (const file of sorted) {
|
|
3567
3691
|
h.update(file.name);
|
|
@@ -3633,14 +3757,15 @@ function triggerSkillsScan() {
|
|
|
3633
3757
|
function getSkillsDir() {
|
|
3634
3758
|
return skillsDir;
|
|
3635
3759
|
}
|
|
3636
|
-
function addToApprovedList(skillName, publisher, hash) {
|
|
3760
|
+
function addToApprovedList(skillName, publisher, hash, slug) {
|
|
3637
3761
|
const approved = loadApprovedSkills();
|
|
3638
3762
|
if (!approved.some((s) => s.name === skillName)) {
|
|
3639
3763
|
approved.push({
|
|
3640
3764
|
name: skillName,
|
|
3641
3765
|
approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3642
3766
|
...publisher ? { publisher } : {},
|
|
3643
|
-
...hash ? { hash } : {}
|
|
3767
|
+
...hash ? { hash } : {},
|
|
3768
|
+
...slug ? { slug } : {}
|
|
3644
3769
|
});
|
|
3645
3770
|
saveApprovedSkills(approved);
|
|
3646
3771
|
}
|
|
@@ -3887,7 +4012,7 @@ async function securityRoutes(app) {
|
|
|
3887
4012
|
// libs/shield-daemon/src/auth/passcode.ts
|
|
3888
4013
|
init_vault();
|
|
3889
4014
|
init_state();
|
|
3890
|
-
import * as
|
|
4015
|
+
import * as crypto5 from "node:crypto";
|
|
3891
4016
|
import { DEFAULT_AUTH_CONFIG as DEFAULT_AUTH_CONFIG2 } from "@agenshield/ipc";
|
|
3892
4017
|
var ITERATIONS = 1e5;
|
|
3893
4018
|
var KEY_LENGTH = 64;
|
|
@@ -3895,8 +4020,8 @@ var DIGEST = "sha512";
|
|
|
3895
4020
|
var SALT_LENGTH = 16;
|
|
3896
4021
|
function hashPasscode(passcode) {
|
|
3897
4022
|
return new Promise((resolve3, reject) => {
|
|
3898
|
-
const salt =
|
|
3899
|
-
|
|
4023
|
+
const salt = crypto5.randomBytes(SALT_LENGTH);
|
|
4024
|
+
crypto5.pbkdf2(passcode, salt, ITERATIONS, KEY_LENGTH, DIGEST, (err, derivedKey) => {
|
|
3900
4025
|
if (err) {
|
|
3901
4026
|
reject(err);
|
|
3902
4027
|
return;
|
|
@@ -3916,12 +4041,12 @@ function verifyPasscode(passcode, storedHash) {
|
|
|
3916
4041
|
const iterations = parseInt(parts[0], 10);
|
|
3917
4042
|
const salt = Buffer.from(parts[1], "base64");
|
|
3918
4043
|
const hash = Buffer.from(parts[2], "base64");
|
|
3919
|
-
|
|
4044
|
+
crypto5.pbkdf2(passcode, salt, iterations, hash.length, DIGEST, (err, derivedKey) => {
|
|
3920
4045
|
if (err) {
|
|
3921
4046
|
reject(err);
|
|
3922
4047
|
return;
|
|
3923
4048
|
}
|
|
3924
|
-
resolve3(
|
|
4049
|
+
resolve3(crypto5.timingSafeEqual(hash, derivedKey));
|
|
3925
4050
|
});
|
|
3926
4051
|
});
|
|
3927
4052
|
}
|
|
@@ -4093,6 +4218,12 @@ data: ${data}
|
|
|
4093
4218
|
|
|
4094
4219
|
`;
|
|
4095
4220
|
}
|
|
4221
|
+
var ALWAYS_FULL_PREFIXES = ["skills:", "exec:", "interceptor:", "security:", "wrappers:", "process:", "config:"];
|
|
4222
|
+
function shouldSendFull(event, authenticated) {
|
|
4223
|
+
if (authenticated) return true;
|
|
4224
|
+
if (event.type === "heartbeat" || event.type === "daemon:status") return true;
|
|
4225
|
+
return ALWAYS_FULL_PREFIXES.some((p) => event.type.startsWith(p));
|
|
4226
|
+
}
|
|
4096
4227
|
async function sseRoutes(app) {
|
|
4097
4228
|
app.get("/sse/events", async (request2, reply) => {
|
|
4098
4229
|
const authenticated = isAuthenticated(request2);
|
|
@@ -4118,7 +4249,7 @@ async function sseRoutes(app) {
|
|
|
4118
4249
|
reply.raw.write(authenticated ? formatSSE(statusEvent) : formatStrippedSSE(statusEvent));
|
|
4119
4250
|
const unsubscribe = daemonEvents.subscribe((event) => {
|
|
4120
4251
|
try {
|
|
4121
|
-
if (event
|
|
4252
|
+
if (shouldSendFull(event, authenticated)) {
|
|
4122
4253
|
reply.raw.write(formatSSE(event));
|
|
4123
4254
|
} else {
|
|
4124
4255
|
reply.raw.write(formatStrippedSSE(event));
|
|
@@ -4166,7 +4297,7 @@ async function sseRoutes(app) {
|
|
|
4166
4297
|
const unsubscribe = daemonEvents.subscribe((event) => {
|
|
4167
4298
|
if (event.type.startsWith(filter) || event.type === "heartbeat" || event.type === "daemon:status") {
|
|
4168
4299
|
try {
|
|
4169
|
-
if (event
|
|
4300
|
+
if (shouldSendFull(event, authenticated)) {
|
|
4170
4301
|
reply.raw.write(formatSSE(event));
|
|
4171
4302
|
} else {
|
|
4172
4303
|
reply.raw.write(formatStrippedSSE(event));
|
|
@@ -7944,6 +8075,7 @@ import { parseSkillMd as parseSkillMd2, extractSkillInfo } from "@agenshield/san
|
|
|
7944
8075
|
import { execWithProgress as execWithProgress3 } from "@agenshield/sandbox";
|
|
7945
8076
|
var SUPPORTED_KINDS = /* @__PURE__ */ new Set(["brew", "npm", "pip"]);
|
|
7946
8077
|
var SAFE_PACKAGE_RE = /^[a-zA-Z0-9@/_.\-]+$/;
|
|
8078
|
+
var SYSTEM_PATH = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
7947
8079
|
function findSkillMdRecursive(dir, depth = 0) {
|
|
7948
8080
|
if (depth > 3) return null;
|
|
7949
8081
|
try {
|
|
@@ -8007,7 +8139,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8007
8139
|
onLog(`Installing brew formula: ${formula}`);
|
|
8008
8140
|
const brewCmd = [
|
|
8009
8141
|
`export HOME="${agentHome}"`,
|
|
8010
|
-
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:$PATH"`,
|
|
8142
|
+
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8011
8143
|
`brew install ${formula}`
|
|
8012
8144
|
].join(" && ");
|
|
8013
8145
|
await execWithProgress3(
|
|
@@ -8031,7 +8163,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8031
8163
|
onLog(`Installing npm package: ${pkg}`);
|
|
8032
8164
|
const npmCmd = [
|
|
8033
8165
|
`export HOME="${agentHome}"`,
|
|
8034
|
-
`export PATH="${agentHome}/bin:$PATH"`,
|
|
8166
|
+
`export PATH="${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8035
8167
|
`source "${agentHome}/.nvm/nvm.sh" 2>/dev/null || true`,
|
|
8036
8168
|
`npm install -g ${pkg}`
|
|
8037
8169
|
].join(" && ");
|
|
@@ -8056,7 +8188,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8056
8188
|
onLog(`Installing pip package: ${pkg}`);
|
|
8057
8189
|
const pipCmd = [
|
|
8058
8190
|
`export HOME="${agentHome}"`,
|
|
8059
|
-
`export PATH="${agentHome}/bin:$PATH"`,
|
|
8191
|
+
`export PATH="${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8060
8192
|
`pip install ${pkg}`
|
|
8061
8193
|
].join(" && ");
|
|
8062
8194
|
await execWithProgress3(
|
|
@@ -8081,7 +8213,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8081
8213
|
try {
|
|
8082
8214
|
const checkCmd = [
|
|
8083
8215
|
`export HOME="${agentHome}"`,
|
|
8084
|
-
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:$PATH"`,
|
|
8216
|
+
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8085
8217
|
`source "${agentHome}/.nvm/nvm.sh" 2>/dev/null || true`,
|
|
8086
8218
|
`which ${bin}`
|
|
8087
8219
|
].join(" && ");
|
|
@@ -8381,18 +8513,22 @@ async function marketplaceRoutes(app) {
|
|
|
8381
8513
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "ash_default";
|
|
8382
8514
|
skillDir = path16.join(skillsDir2, slug);
|
|
8383
8515
|
emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
|
|
8384
|
-
addToApprovedList(slug, publisher);
|
|
8516
|
+
addToApprovedList(slug, publisher, void 0, slug);
|
|
8385
8517
|
logs.push("Skill pre-approved");
|
|
8386
8518
|
emitSkillInstallProgress(slug, "copy", "Writing skill files");
|
|
8519
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
8520
|
+
let content = f.content;
|
|
8521
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
8522
|
+
content = stripEnvFromSkillMd(content);
|
|
8523
|
+
content = await injectInstallationTag(content);
|
|
8524
|
+
}
|
|
8525
|
+
return { ...f, content };
|
|
8526
|
+
}));
|
|
8387
8527
|
const brokerAvailable = await isBrokerAvailable();
|
|
8388
8528
|
if (brokerAvailable) {
|
|
8389
|
-
const sanitizedFiles = files.map((f) => ({
|
|
8390
|
-
name: f.name,
|
|
8391
|
-
content: /SKILL\.md$/i.test(f.name) ? stripEnvFromSkillMd(f.content) : f.content
|
|
8392
|
-
}));
|
|
8393
8529
|
const brokerResult = await installSkillViaBroker(
|
|
8394
8530
|
slug,
|
|
8395
|
-
|
|
8531
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
8396
8532
|
{ createWrapper: true, agentHome, socketGroup }
|
|
8397
8533
|
);
|
|
8398
8534
|
if (!brokerResult.installed) {
|
|
@@ -8412,29 +8548,49 @@ async function marketplaceRoutes(app) {
|
|
|
8412
8548
|
} else {
|
|
8413
8549
|
console.log(`[Marketplace] Broker unavailable, installing ${slug} directly`);
|
|
8414
8550
|
sudoMkdir(skillDir, agentUsername);
|
|
8415
|
-
for (const file of
|
|
8551
|
+
for (const file of taggedFiles) {
|
|
8416
8552
|
const filePath = path16.join(skillDir, file.name);
|
|
8417
8553
|
const fileDir = path16.dirname(filePath);
|
|
8418
8554
|
if (fileDir !== skillDir) {
|
|
8419
8555
|
sudoMkdir(fileDir, agentUsername);
|
|
8420
8556
|
}
|
|
8421
|
-
|
|
8422
|
-
sudoWriteFile(filePath, content, agentUsername);
|
|
8557
|
+
sudoWriteFile(filePath, file.content, agentUsername);
|
|
8423
8558
|
}
|
|
8424
8559
|
createSkillWrapper(slug, binDir);
|
|
8425
|
-
logs.push(`Files written directly: ${
|
|
8560
|
+
logs.push(`Files written directly: ${taggedFiles.length} files`);
|
|
8426
8561
|
logs.push(`Wrapper created: ${path16.join(binDir, slug)}`);
|
|
8427
8562
|
}
|
|
8428
8563
|
let depsSuccess = true;
|
|
8429
8564
|
emitSkillInstallProgress(slug, "deps", "Installing skill dependencies");
|
|
8430
8565
|
try {
|
|
8566
|
+
let depsLineCount = 0;
|
|
8567
|
+
let depsLastEmit = Date.now();
|
|
8568
|
+
let depsLastLine = "";
|
|
8569
|
+
const DEPS_DEBOUNCE_MS = 3e3;
|
|
8570
|
+
const depsOnLog = (msg) => {
|
|
8571
|
+
depsLineCount++;
|
|
8572
|
+
depsLastLine = msg;
|
|
8573
|
+
if (/^(Installing|Found|Verifying)\s/.test(msg)) {
|
|
8574
|
+
emitSkillInstallProgress(slug, "deps", msg);
|
|
8575
|
+
depsLastEmit = Date.now();
|
|
8576
|
+
return;
|
|
8577
|
+
}
|
|
8578
|
+
const now = Date.now();
|
|
8579
|
+
if (now - depsLastEmit >= DEPS_DEBOUNCE_MS) {
|
|
8580
|
+
emitSkillInstallProgress(slug, "deps", `Installing... (${depsLineCount} lines)`);
|
|
8581
|
+
depsLastEmit = now;
|
|
8582
|
+
}
|
|
8583
|
+
};
|
|
8431
8584
|
const depsResult = await executeSkillInstallSteps({
|
|
8432
8585
|
slug,
|
|
8433
8586
|
skillDir,
|
|
8434
8587
|
agentHome,
|
|
8435
8588
|
agentUsername,
|
|
8436
|
-
onLog:
|
|
8589
|
+
onLog: depsOnLog
|
|
8437
8590
|
});
|
|
8591
|
+
if (depsLineCount > 0) {
|
|
8592
|
+
emitSkillInstallProgress(slug, "deps", `Dependency install complete (${depsLineCount} lines processed)`);
|
|
8593
|
+
}
|
|
8438
8594
|
if (depsResult.installed.length > 0) {
|
|
8439
8595
|
logs.push(`Dependencies installed: ${depsResult.installed.join(", ")}`);
|
|
8440
8596
|
}
|
|
@@ -8460,6 +8616,10 @@ async function marketplaceRoutes(app) {
|
|
|
8460
8616
|
updateApprovedHash(slug, hash);
|
|
8461
8617
|
logs.push("Integrity hash recorded");
|
|
8462
8618
|
}
|
|
8619
|
+
try {
|
|
8620
|
+
markDownloadedAsInstalled(slug);
|
|
8621
|
+
} catch {
|
|
8622
|
+
}
|
|
8463
8623
|
installInProgress.delete(slug);
|
|
8464
8624
|
const depsWarnings = depsSuccess ? void 0 : logs.filter((l) => l.startsWith("Dependency"));
|
|
8465
8625
|
daemonEvents.broadcast("skills:installed", { name: slug, analysis: analysisResult, depsWarnings });
|
|
@@ -8637,7 +8797,30 @@ async function skillsRoutes(app) {
|
|
|
8637
8797
|
tags: meta.tags,
|
|
8638
8798
|
analysis: buildAnalysisSummary(name, getCachedAnalysis2(name))
|
|
8639
8799
|
};
|
|
8640
|
-
})
|
|
8800
|
+
}),
|
|
8801
|
+
// Disabled: previously installed marketplace skills that are no longer active
|
|
8802
|
+
...(() => {
|
|
8803
|
+
const allKnown = /* @__PURE__ */ new Set([
|
|
8804
|
+
...approvedNames,
|
|
8805
|
+
...untrustedNames,
|
|
8806
|
+
...workspaceNames
|
|
8807
|
+
]);
|
|
8808
|
+
return listDownloadedSkills().filter((d) => d.wasInstalled && !allKnown.has(d.slug) && !allKnown.has(d.name)).map((d) => {
|
|
8809
|
+
const cached = getCachedAnalysis2(d.slug) || getCachedAnalysis2(d.name);
|
|
8810
|
+
return {
|
|
8811
|
+
name: d.slug,
|
|
8812
|
+
source: "marketplace",
|
|
8813
|
+
status: "disabled",
|
|
8814
|
+
path: "",
|
|
8815
|
+
publisher: d.author,
|
|
8816
|
+
description: d.description,
|
|
8817
|
+
version: d.version,
|
|
8818
|
+
author: d.author,
|
|
8819
|
+
tags: d.tags,
|
|
8820
|
+
analysis: buildAnalysisSummary(d.slug, d.analysis || cached)
|
|
8821
|
+
};
|
|
8822
|
+
});
|
|
8823
|
+
})()
|
|
8641
8824
|
];
|
|
8642
8825
|
return reply.send({ data });
|
|
8643
8826
|
});
|
|
@@ -8711,7 +8894,7 @@ async function skillsRoutes(app) {
|
|
|
8711
8894
|
summary = {
|
|
8712
8895
|
name: dlMeta.slug ?? name,
|
|
8713
8896
|
source: "marketplace",
|
|
8714
|
-
status: "downloaded",
|
|
8897
|
+
status: dlMeta.wasInstalled ? "disabled" : "downloaded",
|
|
8715
8898
|
description: dlMeta.description,
|
|
8716
8899
|
path: "",
|
|
8717
8900
|
publisher: dlMeta.author,
|
|
@@ -8978,7 +9161,7 @@ async function skillsRoutes(app) {
|
|
|
8978
9161
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
8979
9162
|
removeFromApprovedList(name);
|
|
8980
9163
|
try {
|
|
8981
|
-
|
|
9164
|
+
markDownloadedAsInstalled(name);
|
|
8982
9165
|
} catch {
|
|
8983
9166
|
}
|
|
8984
9167
|
console.log(`[Skills] Disabled marketplace skill: ${name}`);
|
|
@@ -8998,12 +9181,20 @@ async function skillsRoutes(app) {
|
|
|
8998
9181
|
return reply.code(404).send({ error: "No files in download cache for this skill" });
|
|
8999
9182
|
}
|
|
9000
9183
|
try {
|
|
9001
|
-
addToApprovedList(name, meta.author);
|
|
9184
|
+
addToApprovedList(name, meta.author, void 0, meta.slug);
|
|
9185
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9186
|
+
let content = f.content;
|
|
9187
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9188
|
+
content = stripEnvFromSkillMd2(content);
|
|
9189
|
+
content = await injectInstallationTag(content);
|
|
9190
|
+
}
|
|
9191
|
+
return { name: f.name, content, type: f.type };
|
|
9192
|
+
}));
|
|
9002
9193
|
const brokerAvailable = await isBrokerAvailable();
|
|
9003
9194
|
if (brokerAvailable) {
|
|
9004
9195
|
const brokerResult = await installSkillViaBroker(
|
|
9005
9196
|
name,
|
|
9006
|
-
|
|
9197
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
9007
9198
|
{ createWrapper: true, agentHome, socketGroup }
|
|
9008
9199
|
);
|
|
9009
9200
|
if (!brokerResult.installed) {
|
|
@@ -9016,7 +9207,7 @@ async function skillsRoutes(app) {
|
|
|
9016
9207
|
}
|
|
9017
9208
|
} else {
|
|
9018
9209
|
fs17.mkdirSync(skillDir, { recursive: true });
|
|
9019
|
-
for (const file of
|
|
9210
|
+
for (const file of taggedFiles) {
|
|
9020
9211
|
const filePath = path17.join(skillDir, file.name);
|
|
9021
9212
|
fs17.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
9022
9213
|
fs17.writeFileSync(filePath, file.content, "utf-8");
|
|
@@ -9033,6 +9224,10 @@ async function skillsRoutes(app) {
|
|
|
9033
9224
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
9034
9225
|
const hash = computeSkillHash(skillDir);
|
|
9035
9226
|
if (hash) updateApprovedHash(name, hash);
|
|
9227
|
+
try {
|
|
9228
|
+
markDownloadedAsInstalled(name);
|
|
9229
|
+
} catch {
|
|
9230
|
+
}
|
|
9036
9231
|
console.log(`[Skills] Enabled marketplace skill: ${name}`);
|
|
9037
9232
|
return reply.send({ success: true, action: "enabled", name });
|
|
9038
9233
|
} catch (err) {
|
|
@@ -9082,12 +9277,19 @@ async function skillsRoutes(app) {
|
|
|
9082
9277
|
const skillDir = path17.join(skillsDir2, name);
|
|
9083
9278
|
try {
|
|
9084
9279
|
addToApprovedList(name, publisher);
|
|
9280
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9281
|
+
let content = f.content;
|
|
9282
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9283
|
+
content = stripEnvFromSkillMd2(content);
|
|
9284
|
+
content = await injectInstallationTag(content);
|
|
9285
|
+
}
|
|
9286
|
+
return { name: f.name, content };
|
|
9287
|
+
}));
|
|
9085
9288
|
sudoMkdir(skillDir, agentUsername);
|
|
9086
|
-
for (const file of
|
|
9289
|
+
for (const file of taggedFiles) {
|
|
9087
9290
|
const filePath = path17.join(skillDir, file.name);
|
|
9088
9291
|
sudoMkdir(path17.dirname(filePath), agentUsername);
|
|
9089
|
-
|
|
9090
|
-
sudoWriteFile(filePath, content, agentUsername);
|
|
9292
|
+
sudoWriteFile(filePath, file.content, agentUsername);
|
|
9091
9293
|
}
|
|
9092
9294
|
try {
|
|
9093
9295
|
execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
@@ -9139,12 +9341,20 @@ async function skillsRoutes(app) {
|
|
|
9139
9341
|
const binDir = path17.join(agentHome, "bin");
|
|
9140
9342
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "ash_default";
|
|
9141
9343
|
const skillDir = path17.join(getSkillsDir(), name);
|
|
9142
|
-
addToApprovedList(name, meta.author);
|
|
9344
|
+
addToApprovedList(name, meta.author, void 0, meta.slug);
|
|
9345
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9346
|
+
let content = f.content;
|
|
9347
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9348
|
+
content = stripEnvFromSkillMd2(content);
|
|
9349
|
+
content = await injectInstallationTag(content);
|
|
9350
|
+
}
|
|
9351
|
+
return { name: f.name, content, type: f.type };
|
|
9352
|
+
}));
|
|
9143
9353
|
const brokerAvailable = await isBrokerAvailable();
|
|
9144
9354
|
if (brokerAvailable) {
|
|
9145
9355
|
const brokerResult = await installSkillViaBroker(
|
|
9146
9356
|
name,
|
|
9147
|
-
|
|
9357
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
9148
9358
|
{ createWrapper: true, agentHome, socketGroup }
|
|
9149
9359
|
);
|
|
9150
9360
|
if (!brokerResult.installed) {
|
|
@@ -9157,7 +9367,7 @@ async function skillsRoutes(app) {
|
|
|
9157
9367
|
}
|
|
9158
9368
|
} else {
|
|
9159
9369
|
fs17.mkdirSync(skillDir, { recursive: true });
|
|
9160
|
-
for (const file of
|
|
9370
|
+
for (const file of taggedFiles) {
|
|
9161
9371
|
const filePath = path17.join(skillDir, file.name);
|
|
9162
9372
|
fs17.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
9163
9373
|
fs17.writeFileSync(filePath, file.content, "utf-8");
|
|
@@ -9174,6 +9384,10 @@ async function skillsRoutes(app) {
|
|
|
9174
9384
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
9175
9385
|
const unblockHash = computeSkillHash(path17.join(getSkillsDir(), name));
|
|
9176
9386
|
if (unblockHash) updateApprovedHash(name, unblockHash);
|
|
9387
|
+
try {
|
|
9388
|
+
markDownloadedAsInstalled(name);
|
|
9389
|
+
} catch {
|
|
9390
|
+
}
|
|
9177
9391
|
console.log(`[Skills] Unblocked and installed skill: ${name}`);
|
|
9178
9392
|
return reply.send({ success: true, message: `Skill "${name}" approved and installed` });
|
|
9179
9393
|
} catch (err) {
|
|
@@ -9673,7 +9887,7 @@ async function authRoutes(app) {
|
|
|
9673
9887
|
init_vault();
|
|
9674
9888
|
import { isSecretEnvVar } from "@agenshield/sandbox";
|
|
9675
9889
|
init_secret_sync();
|
|
9676
|
-
import
|
|
9890
|
+
import crypto6 from "node:crypto";
|
|
9677
9891
|
function maskValue(value) {
|
|
9678
9892
|
if (value.length <= 4) return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
9679
9893
|
return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" + value.slice(-4);
|
|
@@ -9768,7 +9982,7 @@ async function secretsRoutes(app) {
|
|
|
9768
9982
|
const secrets = await vault.get("secrets") ?? [];
|
|
9769
9983
|
const resolvedScope = scope ?? (policyIds?.length > 0 ? "policed" : "global");
|
|
9770
9984
|
const newSecret = {
|
|
9771
|
-
id:
|
|
9985
|
+
id: crypto6.randomUUID(),
|
|
9772
9986
|
name: name.trim(),
|
|
9773
9987
|
value,
|
|
9774
9988
|
policyIds: resolvedScope === "standalone" ? [] : policyIds ?? [],
|
|
@@ -10069,7 +10283,7 @@ async function openclawRoutes(app) {
|
|
|
10069
10283
|
}
|
|
10070
10284
|
|
|
10071
10285
|
// libs/shield-daemon/src/routes/rpc.ts
|
|
10072
|
-
import * as
|
|
10286
|
+
import * as crypto7 from "node:crypto";
|
|
10073
10287
|
import * as nodefs from "node:fs";
|
|
10074
10288
|
|
|
10075
10289
|
// libs/shield-daemon/src/policy/url-matcher.ts
|
|
@@ -10183,7 +10397,7 @@ function checkUrlPolicy(policies, url) {
|
|
|
10183
10397
|
// libs/shield-daemon/src/proxy/server.ts
|
|
10184
10398
|
import * as http from "node:http";
|
|
10185
10399
|
import * as net from "node:net";
|
|
10186
|
-
function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
10400
|
+
function createPerRunProxy(urlPolicies, onActivity, logger, onBlock) {
|
|
10187
10401
|
const server = http.createServer((req, res) => {
|
|
10188
10402
|
onActivity();
|
|
10189
10403
|
const url = req.url;
|
|
@@ -10195,6 +10409,7 @@ function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
|
10195
10409
|
const allowed = checkUrlPolicy(urlPolicies, url);
|
|
10196
10410
|
if (!allowed) {
|
|
10197
10411
|
logger(`BLOCKED HTTP ${req.method} ${url}`);
|
|
10412
|
+
onBlock?.(req.method || "GET", url, "http");
|
|
10198
10413
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
10199
10414
|
res.end("Blocked by AgenShield URL policy");
|
|
10200
10415
|
return;
|
|
@@ -10238,6 +10453,7 @@ function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
|
10238
10453
|
const allowed = checkUrlPolicy(urlPolicies, `https://${hostname2}`);
|
|
10239
10454
|
if (!allowed) {
|
|
10240
10455
|
logger(`BLOCKED CONNECT ${hostname2}:${port}`);
|
|
10456
|
+
onBlock?.("CONNECT", `${hostname2}:${port}`, "https");
|
|
10241
10457
|
clientSocket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
10242
10458
|
clientSocket.destroy();
|
|
10243
10459
|
return;
|
|
@@ -10300,7 +10516,16 @@ var ProxyPool = class {
|
|
|
10300
10516
|
const logger = (msg) => {
|
|
10301
10517
|
console.log(`[proxy:${execId.slice(0, 8)}] ${msg}`);
|
|
10302
10518
|
};
|
|
10303
|
-
const
|
|
10519
|
+
const onBlock = (method, target, protocol) => {
|
|
10520
|
+
emitInterceptorEvent({
|
|
10521
|
+
type: "denied",
|
|
10522
|
+
operation: "http_request",
|
|
10523
|
+
target: protocol === "https" ? `https://${target}` : target,
|
|
10524
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10525
|
+
error: `Blocked by URL policy (${method})`
|
|
10526
|
+
});
|
|
10527
|
+
};
|
|
10528
|
+
const server = createPerRunProxy(urlPolicies, onActivity, logger, onBlock);
|
|
10304
10529
|
const port = await new Promise((resolve3, reject) => {
|
|
10305
10530
|
server.listen(0, "127.0.0.1", () => {
|
|
10306
10531
|
const addr = server.address();
|
|
@@ -10383,12 +10608,19 @@ function matchCommandPattern(pattern, target) {
|
|
|
10383
10608
|
normalizedTarget = firstSpace >= 0 ? basename6 + target.slice(firstSpace) : basename6;
|
|
10384
10609
|
}
|
|
10385
10610
|
if (trimmed.endsWith(":*")) {
|
|
10386
|
-
|
|
10611
|
+
let prefix = trimmed.slice(0, -2);
|
|
10612
|
+
if (prefix.includes("/")) {
|
|
10613
|
+
prefix = prefix.split("/").pop() || prefix;
|
|
10614
|
+
}
|
|
10387
10615
|
const lowerTarget = normalizedTarget.toLowerCase();
|
|
10388
10616
|
const lowerPrefix = prefix.toLowerCase();
|
|
10389
10617
|
return lowerTarget === lowerPrefix || lowerTarget.startsWith(lowerPrefix + " ");
|
|
10390
10618
|
}
|
|
10391
|
-
|
|
10619
|
+
let normalizedPattern = trimmed;
|
|
10620
|
+
if (trimmed.includes("/")) {
|
|
10621
|
+
normalizedPattern = trimmed.split("/").pop() || trimmed;
|
|
10622
|
+
}
|
|
10623
|
+
return normalizedTarget.toLowerCase() === normalizedPattern.toLowerCase();
|
|
10392
10624
|
}
|
|
10393
10625
|
function operationToTarget(operation) {
|
|
10394
10626
|
switch (operation) {
|
|
@@ -10481,7 +10713,7 @@ async function buildSandboxConfig(config, matchedPolicy, _context, target) {
|
|
|
10481
10713
|
console.log(`[sandbox] direct network access (no proxy)`);
|
|
10482
10714
|
sandbox.networkAllowed = true;
|
|
10483
10715
|
} else if (networkMode === "proxy") {
|
|
10484
|
-
const execId =
|
|
10716
|
+
const execId = crypto7.randomUUID();
|
|
10485
10717
|
const commandBasename = extractCommandBasename(target || "");
|
|
10486
10718
|
const urlPolicies = filterUrlPoliciesForCommand(config.policies || [], commandBasename);
|
|
10487
10719
|
const pool = getProxyPool();
|
|
@@ -10546,7 +10778,11 @@ async function evaluatePolicyCheck(operation, target, context) {
|
|
|
10546
10778
|
} else if (targetType === "command") {
|
|
10547
10779
|
matches = matchCommandPattern(pattern, effectiveTarget);
|
|
10548
10780
|
} else {
|
|
10549
|
-
|
|
10781
|
+
let fsPattern = pattern;
|
|
10782
|
+
if (targetType === "filesystem" && fsPattern.endsWith("/")) {
|
|
10783
|
+
fsPattern = fsPattern + "**";
|
|
10784
|
+
}
|
|
10785
|
+
const regex = globToRegex(fsPattern);
|
|
10550
10786
|
matches = regex.test(effectiveTarget);
|
|
10551
10787
|
}
|
|
10552
10788
|
console.log("[policy_check] pattern:", pattern, "-> base:", targetType === "url" ? normalizeUrlBase(pattern) : pattern, "| target:", effectiveTarget, "| matches:", matches);
|
|
@@ -10612,12 +10848,29 @@ async function handleHttpRequest(params) {
|
|
|
10612
10848
|
body: responseBody
|
|
10613
10849
|
};
|
|
10614
10850
|
}
|
|
10851
|
+
async function handlePolicyCheck(params) {
|
|
10852
|
+
const operation = String(params["operation"] ?? "");
|
|
10853
|
+
const target = String(params["target"] ?? "");
|
|
10854
|
+
const context = params["context"];
|
|
10855
|
+
const result = await evaluatePolicyCheck(operation, target, context);
|
|
10856
|
+
if (!result.allowed) {
|
|
10857
|
+
if (operation === "exec") {
|
|
10858
|
+
emitExecDenied(target, result.reason || "Denied by policy");
|
|
10859
|
+
} else {
|
|
10860
|
+
emitInterceptorEvent({
|
|
10861
|
+
type: "denied",
|
|
10862
|
+
operation,
|
|
10863
|
+
target,
|
|
10864
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10865
|
+
policyId: result.policyId,
|
|
10866
|
+
error: result.reason || "Denied by policy"
|
|
10867
|
+
});
|
|
10868
|
+
}
|
|
10869
|
+
}
|
|
10870
|
+
return result;
|
|
10871
|
+
}
|
|
10615
10872
|
var handlers = {
|
|
10616
|
-
policy_check: (params) =>
|
|
10617
|
-
String(params["operation"] ?? ""),
|
|
10618
|
-
String(params["target"] ?? ""),
|
|
10619
|
-
params["context"]
|
|
10620
|
-
),
|
|
10873
|
+
policy_check: (params) => handlePolicyCheck(params),
|
|
10621
10874
|
events_batch: (params) => handleEventsBatch(params),
|
|
10622
10875
|
http_request: (params) => handleHttpRequest(params),
|
|
10623
10876
|
ping: () => ({ status: "ok" })
|
|
@@ -10842,6 +11095,12 @@ async function startServer(config) {
|
|
|
10842
11095
|
fs23.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
|
|
10843
11096
|
console.log(`[Daemon] Created skills directory: ${skillsDir2}`);
|
|
10844
11097
|
}
|
|
11098
|
+
try {
|
|
11099
|
+
await getInstallationKey();
|
|
11100
|
+
console.log("[Daemon] Installation key ready");
|
|
11101
|
+
} catch (err) {
|
|
11102
|
+
console.warn("[Daemon] Failed to initialize installation key:", err.message);
|
|
11103
|
+
}
|
|
10845
11104
|
startSkillsWatcher(skillsDir2, {
|
|
10846
11105
|
onUntrustedDetected: (info) => emitSkillUntrustedDetected(info.name, info.reason),
|
|
10847
11106
|
onApproved: (name) => emitSkillApproved(name)
|