@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/main.js
CHANGED
|
@@ -1293,6 +1293,41 @@ var init_crypto = __esm({
|
|
|
1293
1293
|
}
|
|
1294
1294
|
});
|
|
1295
1295
|
|
|
1296
|
+
// libs/shield-daemon/src/vault/installation-key.ts
|
|
1297
|
+
import * as crypto2 from "node:crypto";
|
|
1298
|
+
async function getInstallationKey() {
|
|
1299
|
+
if (cachedKey) return cachedKey;
|
|
1300
|
+
const vault = getVault();
|
|
1301
|
+
const contents = await vault.load();
|
|
1302
|
+
if (contents.installationKey) {
|
|
1303
|
+
cachedKey = contents.installationKey;
|
|
1304
|
+
return cachedKey;
|
|
1305
|
+
}
|
|
1306
|
+
const key = crypto2.randomBytes(32).toString("hex");
|
|
1307
|
+
await vault.set("installationKey", key);
|
|
1308
|
+
cachedKey = key;
|
|
1309
|
+
console.log("[InstallationKey] Generated new installation key");
|
|
1310
|
+
return key;
|
|
1311
|
+
}
|
|
1312
|
+
async function getInstallationTag() {
|
|
1313
|
+
const key = await getInstallationKey();
|
|
1314
|
+
return `${INSTALLATION_KEY_PREFIX}${key}`;
|
|
1315
|
+
}
|
|
1316
|
+
function hasValidInstallationTagSync(tags) {
|
|
1317
|
+
if (!cachedKey) return false;
|
|
1318
|
+
const fullTag = `${INSTALLATION_KEY_PREFIX}${cachedKey}`;
|
|
1319
|
+
return tags.some((tag) => tag === fullTag);
|
|
1320
|
+
}
|
|
1321
|
+
var INSTALLATION_KEY_PREFIX, cachedKey;
|
|
1322
|
+
var init_installation_key = __esm({
|
|
1323
|
+
"libs/shield-daemon/src/vault/installation-key.ts"() {
|
|
1324
|
+
"use strict";
|
|
1325
|
+
init_vault();
|
|
1326
|
+
INSTALLATION_KEY_PREFIX = "agenshield-";
|
|
1327
|
+
cachedKey = null;
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
|
|
1296
1331
|
// libs/shield-daemon/src/vault/index.ts
|
|
1297
1332
|
import * as fs4 from "node:fs";
|
|
1298
1333
|
import * as path4 from "node:path";
|
|
@@ -1310,6 +1345,7 @@ var init_vault = __esm({
|
|
|
1310
1345
|
init_crypto();
|
|
1311
1346
|
init_paths();
|
|
1312
1347
|
init_crypto();
|
|
1348
|
+
init_installation_key();
|
|
1313
1349
|
Vault = class {
|
|
1314
1350
|
key;
|
|
1315
1351
|
vaultPath;
|
|
@@ -1847,7 +1883,7 @@ init_state();
|
|
|
1847
1883
|
init_vault();
|
|
1848
1884
|
|
|
1849
1885
|
// libs/shield-daemon/src/auth/session.ts
|
|
1850
|
-
import * as
|
|
1886
|
+
import * as crypto3 from "node:crypto";
|
|
1851
1887
|
import { DEFAULT_AUTH_CONFIG } from "@agenshield/ipc";
|
|
1852
1888
|
var SessionManager = class {
|
|
1853
1889
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -1861,7 +1897,7 @@ var SessionManager = class {
|
|
|
1861
1897
|
* Generate a secure random token
|
|
1862
1898
|
*/
|
|
1863
1899
|
generateToken() {
|
|
1864
|
-
return
|
|
1900
|
+
return crypto3.randomBytes(32).toString("base64url");
|
|
1865
1901
|
}
|
|
1866
1902
|
/**
|
|
1867
1903
|
* Create a new session
|
|
@@ -2431,7 +2467,7 @@ function generatePolicyMarkdown(policies, knownSkills) {
|
|
|
2431
2467
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
2432
2468
|
import * as fs11 from "node:fs";
|
|
2433
2469
|
import * as path11 from "node:path";
|
|
2434
|
-
import * as
|
|
2470
|
+
import * as crypto4 from "node:crypto";
|
|
2435
2471
|
import { parseSkillMd } from "@agenshield/sandbox";
|
|
2436
2472
|
|
|
2437
2473
|
// libs/shield-daemon/src/services/marketplace.ts
|
|
@@ -2620,6 +2656,17 @@ function updateDownloadedAnalysis(slug, analysis) {
|
|
|
2620
2656
|
} catch {
|
|
2621
2657
|
}
|
|
2622
2658
|
}
|
|
2659
|
+
function markDownloadedAsInstalled(slug) {
|
|
2660
|
+
const metaPath = path9.join(getMarketplaceDir(), slug, "metadata.json");
|
|
2661
|
+
try {
|
|
2662
|
+
if (!fs9.existsSync(metaPath)) return;
|
|
2663
|
+
const meta = JSON.parse(fs9.readFileSync(metaPath, "utf-8"));
|
|
2664
|
+
if (meta.wasInstalled) return;
|
|
2665
|
+
meta.wasInstalled = true;
|
|
2666
|
+
fs9.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
2667
|
+
} catch {
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2623
2670
|
function listDownloadedSkills() {
|
|
2624
2671
|
const baseDir = getMarketplaceDir();
|
|
2625
2672
|
if (!fs9.existsSync(baseDir)) return [];
|
|
@@ -2640,7 +2687,8 @@ function listDownloadedSkills() {
|
|
|
2640
2687
|
tags: meta.tags ?? [],
|
|
2641
2688
|
hasAnalysis: !!meta.analysis,
|
|
2642
2689
|
source: meta.source,
|
|
2643
|
-
analysis: meta.analysis
|
|
2690
|
+
analysis: meta.analysis,
|
|
2691
|
+
wasInstalled: meta.wasInstalled ?? false
|
|
2644
2692
|
});
|
|
2645
2693
|
} catch {
|
|
2646
2694
|
}
|
|
@@ -3237,6 +3285,9 @@ function emitSkillUntrustedDetected(name, reason) {
|
|
|
3237
3285
|
function emitSkillApproved(skillName) {
|
|
3238
3286
|
daemonEvents.broadcast("skills:approved", { name: skillName });
|
|
3239
3287
|
}
|
|
3288
|
+
function emitExecDenied(command, reason) {
|
|
3289
|
+
daemonEvents.broadcast("exec:denied", { command, reason });
|
|
3290
|
+
}
|
|
3240
3291
|
function emitAgenCoAuthRequired(authUrl, integration) {
|
|
3241
3292
|
daemonEvents.broadcast("agenco:auth_required", { authUrl, integration });
|
|
3242
3293
|
}
|
|
@@ -3276,6 +3327,56 @@ function emitEvent(type, data) {
|
|
|
3276
3327
|
|
|
3277
3328
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
3278
3329
|
init_paths();
|
|
3330
|
+
|
|
3331
|
+
// libs/shield-daemon/src/services/skill-tag-injector.ts
|
|
3332
|
+
init_installation_key();
|
|
3333
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
3334
|
+
var FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
|
|
3335
|
+
async function injectInstallationTag(content) {
|
|
3336
|
+
const tag = await getInstallationTag();
|
|
3337
|
+
const match = content.match(FRONTMATTER_RE);
|
|
3338
|
+
if (!match) {
|
|
3339
|
+
return `---
|
|
3340
|
+
tags:
|
|
3341
|
+
- ${tag}
|
|
3342
|
+
---
|
|
3343
|
+
${content}`;
|
|
3344
|
+
}
|
|
3345
|
+
try {
|
|
3346
|
+
const metadata = parseYaml(match[1]);
|
|
3347
|
+
if (!metadata || typeof metadata !== "object") {
|
|
3348
|
+
return content;
|
|
3349
|
+
}
|
|
3350
|
+
if (!Array.isArray(metadata.tags)) {
|
|
3351
|
+
metadata.tags = [];
|
|
3352
|
+
}
|
|
3353
|
+
metadata.tags = metadata.tags.filter(
|
|
3354
|
+
(t) => typeof t !== "string" || !t.startsWith("agenshield-")
|
|
3355
|
+
);
|
|
3356
|
+
metadata.tags.push(tag);
|
|
3357
|
+
return `---
|
|
3358
|
+
${stringifyYaml(metadata).trimEnd()}
|
|
3359
|
+
---
|
|
3360
|
+
${match[2]}`;
|
|
3361
|
+
} catch {
|
|
3362
|
+
return content;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
function extractTagsFromSkillMd(content) {
|
|
3366
|
+
const match = content.match(FRONTMATTER_RE);
|
|
3367
|
+
if (!match) return [];
|
|
3368
|
+
try {
|
|
3369
|
+
const metadata = parseYaml(match[1]);
|
|
3370
|
+
if (!metadata || typeof metadata !== "object") return [];
|
|
3371
|
+
if (!Array.isArray(metadata.tags)) return [];
|
|
3372
|
+
return metadata.tags.filter((t) => typeof t === "string");
|
|
3373
|
+
} catch {
|
|
3374
|
+
return [];
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
// libs/shield-daemon/src/watchers/skills.ts
|
|
3379
|
+
init_installation_key();
|
|
3279
3380
|
function getApprovedSkillsPath() {
|
|
3280
3381
|
return path11.join(getSystemConfigDir(), "approved-skills.json");
|
|
3281
3382
|
}
|
|
@@ -3414,7 +3515,7 @@ function computeSkillHash(skillDir) {
|
|
|
3414
3515
|
const files = readSkillFiles(skillDir);
|
|
3415
3516
|
if (files.length === 0) return null;
|
|
3416
3517
|
files.sort((a, b) => a.name.localeCompare(b.name));
|
|
3417
|
-
const hash =
|
|
3518
|
+
const hash = crypto4.createHash("sha256");
|
|
3418
3519
|
for (const file of files) {
|
|
3419
3520
|
hash.update(file.name);
|
|
3420
3521
|
hash.update(file.content);
|
|
@@ -3443,10 +3544,33 @@ function scanSkills() {
|
|
|
3443
3544
|
const approvedEntry = approvedMap.get(skillName);
|
|
3444
3545
|
if (!approvedEntry) {
|
|
3445
3546
|
const fullPath = path11.join(skillsDir, skillName);
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3547
|
+
let autoApproved = false;
|
|
3548
|
+
for (const mdName of ["SKILL.md", "skill.md"]) {
|
|
3549
|
+
const mdPath = path11.join(fullPath, mdName);
|
|
3550
|
+
try {
|
|
3551
|
+
if (fs11.existsSync(mdPath)) {
|
|
3552
|
+
const content = fs11.readFileSync(mdPath, "utf-8");
|
|
3553
|
+
const tags = extractTagsFromSkillMd(content);
|
|
3554
|
+
if (hasValidInstallationTagSync(tags)) {
|
|
3555
|
+
console.log(`[SkillsWatcher] Auto-approving skill with valid installation tag: ${skillName}`);
|
|
3556
|
+
const hash = computeSkillHash(fullPath);
|
|
3557
|
+
addToApprovedList(skillName, void 0, hash ?? void 0);
|
|
3558
|
+
if (callbacks.onApproved) {
|
|
3559
|
+
callbacks.onApproved(skillName);
|
|
3560
|
+
}
|
|
3561
|
+
autoApproved = true;
|
|
3562
|
+
break;
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
} catch {
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
if (!autoApproved) {
|
|
3569
|
+
const slug = moveToMarketplace(skillName, fullPath);
|
|
3570
|
+
if (slug) {
|
|
3571
|
+
if (callbacks.onUntrustedDetected) {
|
|
3572
|
+
callbacks.onUntrustedDetected({ name: skillName, reason: "Skill not in approved list" });
|
|
3573
|
+
}
|
|
3450
3574
|
}
|
|
3451
3575
|
}
|
|
3452
3576
|
} else if (approvedEntry.hash) {
|
|
@@ -3552,7 +3676,7 @@ function approveSkill(skillName) {
|
|
|
3552
3676
|
const cachedFiles = getDownloadedSkillFiles(slug);
|
|
3553
3677
|
let hash;
|
|
3554
3678
|
if (cachedFiles.length > 0) {
|
|
3555
|
-
const h =
|
|
3679
|
+
const h = crypto4.createHash("sha256");
|
|
3556
3680
|
const sorted = [...cachedFiles].sort((a, b) => a.name.localeCompare(b.name));
|
|
3557
3681
|
for (const file of sorted) {
|
|
3558
3682
|
h.update(file.name);
|
|
@@ -3621,14 +3745,15 @@ function listApproved() {
|
|
|
3621
3745
|
function getSkillsDir() {
|
|
3622
3746
|
return skillsDir;
|
|
3623
3747
|
}
|
|
3624
|
-
function addToApprovedList(skillName, publisher, hash) {
|
|
3748
|
+
function addToApprovedList(skillName, publisher, hash, slug) {
|
|
3625
3749
|
const approved = loadApprovedSkills();
|
|
3626
3750
|
if (!approved.some((s) => s.name === skillName)) {
|
|
3627
3751
|
approved.push({
|
|
3628
3752
|
name: skillName,
|
|
3629
3753
|
approvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3630
3754
|
...publisher ? { publisher } : {},
|
|
3631
|
-
...hash ? { hash } : {}
|
|
3755
|
+
...hash ? { hash } : {},
|
|
3756
|
+
...slug ? { slug } : {}
|
|
3632
3757
|
});
|
|
3633
3758
|
saveApprovedSkills(approved);
|
|
3634
3759
|
}
|
|
@@ -3875,7 +4000,7 @@ async function securityRoutes(app) {
|
|
|
3875
4000
|
// libs/shield-daemon/src/auth/passcode.ts
|
|
3876
4001
|
init_vault();
|
|
3877
4002
|
init_state();
|
|
3878
|
-
import * as
|
|
4003
|
+
import * as crypto5 from "node:crypto";
|
|
3879
4004
|
import { DEFAULT_AUTH_CONFIG as DEFAULT_AUTH_CONFIG2 } from "@agenshield/ipc";
|
|
3880
4005
|
var ITERATIONS = 1e5;
|
|
3881
4006
|
var KEY_LENGTH = 64;
|
|
@@ -3883,8 +4008,8 @@ var DIGEST = "sha512";
|
|
|
3883
4008
|
var SALT_LENGTH = 16;
|
|
3884
4009
|
function hashPasscode(passcode) {
|
|
3885
4010
|
return new Promise((resolve3, reject) => {
|
|
3886
|
-
const salt =
|
|
3887
|
-
|
|
4011
|
+
const salt = crypto5.randomBytes(SALT_LENGTH);
|
|
4012
|
+
crypto5.pbkdf2(passcode, salt, ITERATIONS, KEY_LENGTH, DIGEST, (err, derivedKey) => {
|
|
3888
4013
|
if (err) {
|
|
3889
4014
|
reject(err);
|
|
3890
4015
|
return;
|
|
@@ -3904,12 +4029,12 @@ function verifyPasscode(passcode, storedHash) {
|
|
|
3904
4029
|
const iterations = parseInt(parts[0], 10);
|
|
3905
4030
|
const salt = Buffer.from(parts[1], "base64");
|
|
3906
4031
|
const hash = Buffer.from(parts[2], "base64");
|
|
3907
|
-
|
|
4032
|
+
crypto5.pbkdf2(passcode, salt, iterations, hash.length, DIGEST, (err, derivedKey) => {
|
|
3908
4033
|
if (err) {
|
|
3909
4034
|
reject(err);
|
|
3910
4035
|
return;
|
|
3911
4036
|
}
|
|
3912
|
-
resolve3(
|
|
4037
|
+
resolve3(crypto5.timingSafeEqual(hash, derivedKey));
|
|
3913
4038
|
});
|
|
3914
4039
|
});
|
|
3915
4040
|
}
|
|
@@ -4081,6 +4206,12 @@ data: ${data}
|
|
|
4081
4206
|
|
|
4082
4207
|
`;
|
|
4083
4208
|
}
|
|
4209
|
+
var ALWAYS_FULL_PREFIXES = ["skills:", "exec:", "interceptor:", "security:", "wrappers:", "process:", "config:"];
|
|
4210
|
+
function shouldSendFull(event, authenticated) {
|
|
4211
|
+
if (authenticated) return true;
|
|
4212
|
+
if (event.type === "heartbeat" || event.type === "daemon:status") return true;
|
|
4213
|
+
return ALWAYS_FULL_PREFIXES.some((p) => event.type.startsWith(p));
|
|
4214
|
+
}
|
|
4084
4215
|
async function sseRoutes(app) {
|
|
4085
4216
|
app.get("/sse/events", async (request2, reply) => {
|
|
4086
4217
|
const authenticated = isAuthenticated(request2);
|
|
@@ -4106,7 +4237,7 @@ async function sseRoutes(app) {
|
|
|
4106
4237
|
reply.raw.write(authenticated ? formatSSE(statusEvent) : formatStrippedSSE(statusEvent));
|
|
4107
4238
|
const unsubscribe = daemonEvents.subscribe((event) => {
|
|
4108
4239
|
try {
|
|
4109
|
-
if (event
|
|
4240
|
+
if (shouldSendFull(event, authenticated)) {
|
|
4110
4241
|
reply.raw.write(formatSSE(event));
|
|
4111
4242
|
} else {
|
|
4112
4243
|
reply.raw.write(formatStrippedSSE(event));
|
|
@@ -4154,7 +4285,7 @@ async function sseRoutes(app) {
|
|
|
4154
4285
|
const unsubscribe = daemonEvents.subscribe((event) => {
|
|
4155
4286
|
if (event.type.startsWith(filter) || event.type === "heartbeat" || event.type === "daemon:status") {
|
|
4156
4287
|
try {
|
|
4157
|
-
if (event
|
|
4288
|
+
if (shouldSendFull(event, authenticated)) {
|
|
4158
4289
|
reply.raw.write(formatSSE(event));
|
|
4159
4290
|
} else {
|
|
4160
4291
|
reply.raw.write(formatStrippedSSE(event));
|
|
@@ -7932,6 +8063,7 @@ import { parseSkillMd as parseSkillMd2, extractSkillInfo } from "@agenshield/san
|
|
|
7932
8063
|
import { execWithProgress as execWithProgress3 } from "@agenshield/sandbox";
|
|
7933
8064
|
var SUPPORTED_KINDS = /* @__PURE__ */ new Set(["brew", "npm", "pip"]);
|
|
7934
8065
|
var SAFE_PACKAGE_RE = /^[a-zA-Z0-9@/_.\-]+$/;
|
|
8066
|
+
var SYSTEM_PATH = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
7935
8067
|
function findSkillMdRecursive(dir, depth = 0) {
|
|
7936
8068
|
if (depth > 3) return null;
|
|
7937
8069
|
try {
|
|
@@ -7995,7 +8127,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
7995
8127
|
onLog(`Installing brew formula: ${formula}`);
|
|
7996
8128
|
const brewCmd = [
|
|
7997
8129
|
`export HOME="${agentHome}"`,
|
|
7998
|
-
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:$PATH"`,
|
|
8130
|
+
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
7999
8131
|
`brew install ${formula}`
|
|
8000
8132
|
].join(" && ");
|
|
8001
8133
|
await execWithProgress3(
|
|
@@ -8019,7 +8151,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8019
8151
|
onLog(`Installing npm package: ${pkg}`);
|
|
8020
8152
|
const npmCmd = [
|
|
8021
8153
|
`export HOME="${agentHome}"`,
|
|
8022
|
-
`export PATH="${agentHome}/bin:$PATH"`,
|
|
8154
|
+
`export PATH="${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8023
8155
|
`source "${agentHome}/.nvm/nvm.sh" 2>/dev/null || true`,
|
|
8024
8156
|
`npm install -g ${pkg}`
|
|
8025
8157
|
].join(" && ");
|
|
@@ -8044,7 +8176,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8044
8176
|
onLog(`Installing pip package: ${pkg}`);
|
|
8045
8177
|
const pipCmd = [
|
|
8046
8178
|
`export HOME="${agentHome}"`,
|
|
8047
|
-
`export PATH="${agentHome}/bin:$PATH"`,
|
|
8179
|
+
`export PATH="${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8048
8180
|
`pip install ${pkg}`
|
|
8049
8181
|
].join(" && ");
|
|
8050
8182
|
await execWithProgress3(
|
|
@@ -8069,7 +8201,7 @@ async function executeSkillInstallSteps(options) {
|
|
|
8069
8201
|
try {
|
|
8070
8202
|
const checkCmd = [
|
|
8071
8203
|
`export HOME="${agentHome}"`,
|
|
8072
|
-
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:$PATH"`,
|
|
8204
|
+
`export PATH="${agentHome}/homebrew/bin:${agentHome}/bin:${SYSTEM_PATH}:$PATH"`,
|
|
8073
8205
|
`source "${agentHome}/.nvm/nvm.sh" 2>/dev/null || true`,
|
|
8074
8206
|
`which ${bin}`
|
|
8075
8207
|
].join(" && ");
|
|
@@ -8369,18 +8501,22 @@ async function marketplaceRoutes(app) {
|
|
|
8369
8501
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "ash_default";
|
|
8370
8502
|
skillDir = path16.join(skillsDir2, slug);
|
|
8371
8503
|
emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
|
|
8372
|
-
addToApprovedList(slug, publisher);
|
|
8504
|
+
addToApprovedList(slug, publisher, void 0, slug);
|
|
8373
8505
|
logs.push("Skill pre-approved");
|
|
8374
8506
|
emitSkillInstallProgress(slug, "copy", "Writing skill files");
|
|
8507
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
8508
|
+
let content = f.content;
|
|
8509
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
8510
|
+
content = stripEnvFromSkillMd(content);
|
|
8511
|
+
content = await injectInstallationTag(content);
|
|
8512
|
+
}
|
|
8513
|
+
return { ...f, content };
|
|
8514
|
+
}));
|
|
8375
8515
|
const brokerAvailable = await isBrokerAvailable();
|
|
8376
8516
|
if (brokerAvailable) {
|
|
8377
|
-
const sanitizedFiles = files.map((f) => ({
|
|
8378
|
-
name: f.name,
|
|
8379
|
-
content: /SKILL\.md$/i.test(f.name) ? stripEnvFromSkillMd(f.content) : f.content
|
|
8380
|
-
}));
|
|
8381
8517
|
const brokerResult = await installSkillViaBroker(
|
|
8382
8518
|
slug,
|
|
8383
|
-
|
|
8519
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
8384
8520
|
{ createWrapper: true, agentHome, socketGroup }
|
|
8385
8521
|
);
|
|
8386
8522
|
if (!brokerResult.installed) {
|
|
@@ -8400,29 +8536,49 @@ async function marketplaceRoutes(app) {
|
|
|
8400
8536
|
} else {
|
|
8401
8537
|
console.log(`[Marketplace] Broker unavailable, installing ${slug} directly`);
|
|
8402
8538
|
sudoMkdir(skillDir, agentUsername);
|
|
8403
|
-
for (const file of
|
|
8539
|
+
for (const file of taggedFiles) {
|
|
8404
8540
|
const filePath = path16.join(skillDir, file.name);
|
|
8405
8541
|
const fileDir = path16.dirname(filePath);
|
|
8406
8542
|
if (fileDir !== skillDir) {
|
|
8407
8543
|
sudoMkdir(fileDir, agentUsername);
|
|
8408
8544
|
}
|
|
8409
|
-
|
|
8410
|
-
sudoWriteFile(filePath, content, agentUsername);
|
|
8545
|
+
sudoWriteFile(filePath, file.content, agentUsername);
|
|
8411
8546
|
}
|
|
8412
8547
|
createSkillWrapper(slug, binDir);
|
|
8413
|
-
logs.push(`Files written directly: ${
|
|
8548
|
+
logs.push(`Files written directly: ${taggedFiles.length} files`);
|
|
8414
8549
|
logs.push(`Wrapper created: ${path16.join(binDir, slug)}`);
|
|
8415
8550
|
}
|
|
8416
8551
|
let depsSuccess = true;
|
|
8417
8552
|
emitSkillInstallProgress(slug, "deps", "Installing skill dependencies");
|
|
8418
8553
|
try {
|
|
8554
|
+
let depsLineCount = 0;
|
|
8555
|
+
let depsLastEmit = Date.now();
|
|
8556
|
+
let depsLastLine = "";
|
|
8557
|
+
const DEPS_DEBOUNCE_MS = 3e3;
|
|
8558
|
+
const depsOnLog = (msg) => {
|
|
8559
|
+
depsLineCount++;
|
|
8560
|
+
depsLastLine = msg;
|
|
8561
|
+
if (/^(Installing|Found|Verifying)\s/.test(msg)) {
|
|
8562
|
+
emitSkillInstallProgress(slug, "deps", msg);
|
|
8563
|
+
depsLastEmit = Date.now();
|
|
8564
|
+
return;
|
|
8565
|
+
}
|
|
8566
|
+
const now = Date.now();
|
|
8567
|
+
if (now - depsLastEmit >= DEPS_DEBOUNCE_MS) {
|
|
8568
|
+
emitSkillInstallProgress(slug, "deps", `Installing... (${depsLineCount} lines)`);
|
|
8569
|
+
depsLastEmit = now;
|
|
8570
|
+
}
|
|
8571
|
+
};
|
|
8419
8572
|
const depsResult = await executeSkillInstallSteps({
|
|
8420
8573
|
slug,
|
|
8421
8574
|
skillDir,
|
|
8422
8575
|
agentHome,
|
|
8423
8576
|
agentUsername,
|
|
8424
|
-
onLog:
|
|
8577
|
+
onLog: depsOnLog
|
|
8425
8578
|
});
|
|
8579
|
+
if (depsLineCount > 0) {
|
|
8580
|
+
emitSkillInstallProgress(slug, "deps", `Dependency install complete (${depsLineCount} lines processed)`);
|
|
8581
|
+
}
|
|
8426
8582
|
if (depsResult.installed.length > 0) {
|
|
8427
8583
|
logs.push(`Dependencies installed: ${depsResult.installed.join(", ")}`);
|
|
8428
8584
|
}
|
|
@@ -8448,6 +8604,10 @@ async function marketplaceRoutes(app) {
|
|
|
8448
8604
|
updateApprovedHash(slug, hash);
|
|
8449
8605
|
logs.push("Integrity hash recorded");
|
|
8450
8606
|
}
|
|
8607
|
+
try {
|
|
8608
|
+
markDownloadedAsInstalled(slug);
|
|
8609
|
+
} catch {
|
|
8610
|
+
}
|
|
8451
8611
|
installInProgress.delete(slug);
|
|
8452
8612
|
const depsWarnings = depsSuccess ? void 0 : logs.filter((l) => l.startsWith("Dependency"));
|
|
8453
8613
|
daemonEvents.broadcast("skills:installed", { name: slug, analysis: analysisResult, depsWarnings });
|
|
@@ -8625,7 +8785,30 @@ async function skillsRoutes(app) {
|
|
|
8625
8785
|
tags: meta.tags,
|
|
8626
8786
|
analysis: buildAnalysisSummary(name, getCachedAnalysis2(name))
|
|
8627
8787
|
};
|
|
8628
|
-
})
|
|
8788
|
+
}),
|
|
8789
|
+
// Disabled: previously installed marketplace skills that are no longer active
|
|
8790
|
+
...(() => {
|
|
8791
|
+
const allKnown = /* @__PURE__ */ new Set([
|
|
8792
|
+
...approvedNames,
|
|
8793
|
+
...untrustedNames,
|
|
8794
|
+
...workspaceNames
|
|
8795
|
+
]);
|
|
8796
|
+
return listDownloadedSkills().filter((d) => d.wasInstalled && !allKnown.has(d.slug) && !allKnown.has(d.name)).map((d) => {
|
|
8797
|
+
const cached = getCachedAnalysis2(d.slug) || getCachedAnalysis2(d.name);
|
|
8798
|
+
return {
|
|
8799
|
+
name: d.slug,
|
|
8800
|
+
source: "marketplace",
|
|
8801
|
+
status: "disabled",
|
|
8802
|
+
path: "",
|
|
8803
|
+
publisher: d.author,
|
|
8804
|
+
description: d.description,
|
|
8805
|
+
version: d.version,
|
|
8806
|
+
author: d.author,
|
|
8807
|
+
tags: d.tags,
|
|
8808
|
+
analysis: buildAnalysisSummary(d.slug, d.analysis || cached)
|
|
8809
|
+
};
|
|
8810
|
+
});
|
|
8811
|
+
})()
|
|
8629
8812
|
];
|
|
8630
8813
|
return reply.send({ data });
|
|
8631
8814
|
});
|
|
@@ -8699,7 +8882,7 @@ async function skillsRoutes(app) {
|
|
|
8699
8882
|
summary = {
|
|
8700
8883
|
name: dlMeta.slug ?? name,
|
|
8701
8884
|
source: "marketplace",
|
|
8702
|
-
status: "downloaded",
|
|
8885
|
+
status: dlMeta.wasInstalled ? "disabled" : "downloaded",
|
|
8703
8886
|
description: dlMeta.description,
|
|
8704
8887
|
path: "",
|
|
8705
8888
|
publisher: dlMeta.author,
|
|
@@ -8966,7 +9149,7 @@ async function skillsRoutes(app) {
|
|
|
8966
9149
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
8967
9150
|
removeFromApprovedList(name);
|
|
8968
9151
|
try {
|
|
8969
|
-
|
|
9152
|
+
markDownloadedAsInstalled(name);
|
|
8970
9153
|
} catch {
|
|
8971
9154
|
}
|
|
8972
9155
|
console.log(`[Skills] Disabled marketplace skill: ${name}`);
|
|
@@ -8986,12 +9169,20 @@ async function skillsRoutes(app) {
|
|
|
8986
9169
|
return reply.code(404).send({ error: "No files in download cache for this skill" });
|
|
8987
9170
|
}
|
|
8988
9171
|
try {
|
|
8989
|
-
addToApprovedList(name, meta.author);
|
|
9172
|
+
addToApprovedList(name, meta.author, void 0, meta.slug);
|
|
9173
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9174
|
+
let content = f.content;
|
|
9175
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9176
|
+
content = stripEnvFromSkillMd2(content);
|
|
9177
|
+
content = await injectInstallationTag(content);
|
|
9178
|
+
}
|
|
9179
|
+
return { name: f.name, content, type: f.type };
|
|
9180
|
+
}));
|
|
8990
9181
|
const brokerAvailable = await isBrokerAvailable();
|
|
8991
9182
|
if (brokerAvailable) {
|
|
8992
9183
|
const brokerResult = await installSkillViaBroker(
|
|
8993
9184
|
name,
|
|
8994
|
-
|
|
9185
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
8995
9186
|
{ createWrapper: true, agentHome, socketGroup }
|
|
8996
9187
|
);
|
|
8997
9188
|
if (!brokerResult.installed) {
|
|
@@ -9004,7 +9195,7 @@ async function skillsRoutes(app) {
|
|
|
9004
9195
|
}
|
|
9005
9196
|
} else {
|
|
9006
9197
|
fs17.mkdirSync(skillDir, { recursive: true });
|
|
9007
|
-
for (const file of
|
|
9198
|
+
for (const file of taggedFiles) {
|
|
9008
9199
|
const filePath = path17.join(skillDir, file.name);
|
|
9009
9200
|
fs17.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
9010
9201
|
fs17.writeFileSync(filePath, file.content, "utf-8");
|
|
@@ -9021,6 +9212,10 @@ async function skillsRoutes(app) {
|
|
|
9021
9212
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
9022
9213
|
const hash = computeSkillHash(skillDir);
|
|
9023
9214
|
if (hash) updateApprovedHash(name, hash);
|
|
9215
|
+
try {
|
|
9216
|
+
markDownloadedAsInstalled(name);
|
|
9217
|
+
} catch {
|
|
9218
|
+
}
|
|
9024
9219
|
console.log(`[Skills] Enabled marketplace skill: ${name}`);
|
|
9025
9220
|
return reply.send({ success: true, action: "enabled", name });
|
|
9026
9221
|
} catch (err) {
|
|
@@ -9070,12 +9265,19 @@ async function skillsRoutes(app) {
|
|
|
9070
9265
|
const skillDir = path17.join(skillsDir2, name);
|
|
9071
9266
|
try {
|
|
9072
9267
|
addToApprovedList(name, publisher);
|
|
9268
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9269
|
+
let content = f.content;
|
|
9270
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9271
|
+
content = stripEnvFromSkillMd2(content);
|
|
9272
|
+
content = await injectInstallationTag(content);
|
|
9273
|
+
}
|
|
9274
|
+
return { name: f.name, content };
|
|
9275
|
+
}));
|
|
9073
9276
|
sudoMkdir(skillDir, agentUsername);
|
|
9074
|
-
for (const file of
|
|
9277
|
+
for (const file of taggedFiles) {
|
|
9075
9278
|
const filePath = path17.join(skillDir, file.name);
|
|
9076
9279
|
sudoMkdir(path17.dirname(filePath), agentUsername);
|
|
9077
|
-
|
|
9078
|
-
sudoWriteFile(filePath, content, agentUsername);
|
|
9280
|
+
sudoWriteFile(filePath, file.content, agentUsername);
|
|
9079
9281
|
}
|
|
9080
9282
|
try {
|
|
9081
9283
|
execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
@@ -9127,12 +9329,20 @@ async function skillsRoutes(app) {
|
|
|
9127
9329
|
const binDir = path17.join(agentHome, "bin");
|
|
9128
9330
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "ash_default";
|
|
9129
9331
|
const skillDir = path17.join(getSkillsDir(), name);
|
|
9130
|
-
addToApprovedList(name, meta.author);
|
|
9332
|
+
addToApprovedList(name, meta.author, void 0, meta.slug);
|
|
9333
|
+
const taggedFiles = await Promise.all(files.map(async (f) => {
|
|
9334
|
+
let content = f.content;
|
|
9335
|
+
if (/SKILL\.md$/i.test(f.name)) {
|
|
9336
|
+
content = stripEnvFromSkillMd2(content);
|
|
9337
|
+
content = await injectInstallationTag(content);
|
|
9338
|
+
}
|
|
9339
|
+
return { name: f.name, content, type: f.type };
|
|
9340
|
+
}));
|
|
9131
9341
|
const brokerAvailable = await isBrokerAvailable();
|
|
9132
9342
|
if (brokerAvailable) {
|
|
9133
9343
|
const brokerResult = await installSkillViaBroker(
|
|
9134
9344
|
name,
|
|
9135
|
-
|
|
9345
|
+
taggedFiles.map((f) => ({ name: f.name, content: f.content })),
|
|
9136
9346
|
{ createWrapper: true, agentHome, socketGroup }
|
|
9137
9347
|
);
|
|
9138
9348
|
if (!brokerResult.installed) {
|
|
@@ -9145,7 +9355,7 @@ async function skillsRoutes(app) {
|
|
|
9145
9355
|
}
|
|
9146
9356
|
} else {
|
|
9147
9357
|
fs17.mkdirSync(skillDir, { recursive: true });
|
|
9148
|
-
for (const file of
|
|
9358
|
+
for (const file of taggedFiles) {
|
|
9149
9359
|
const filePath = path17.join(skillDir, file.name);
|
|
9150
9360
|
fs17.mkdirSync(path17.dirname(filePath), { recursive: true });
|
|
9151
9361
|
fs17.writeFileSync(filePath, file.content, "utf-8");
|
|
@@ -9162,6 +9372,10 @@ async function skillsRoutes(app) {
|
|
|
9162
9372
|
syncOpenClawFromPolicies(loadConfig().policies);
|
|
9163
9373
|
const unblockHash = computeSkillHash(path17.join(getSkillsDir(), name));
|
|
9164
9374
|
if (unblockHash) updateApprovedHash(name, unblockHash);
|
|
9375
|
+
try {
|
|
9376
|
+
markDownloadedAsInstalled(name);
|
|
9377
|
+
} catch {
|
|
9378
|
+
}
|
|
9165
9379
|
console.log(`[Skills] Unblocked and installed skill: ${name}`);
|
|
9166
9380
|
return reply.send({ success: true, message: `Skill "${name}" approved and installed` });
|
|
9167
9381
|
} catch (err) {
|
|
@@ -9661,7 +9875,7 @@ async function authRoutes(app) {
|
|
|
9661
9875
|
init_vault();
|
|
9662
9876
|
import { isSecretEnvVar } from "@agenshield/sandbox";
|
|
9663
9877
|
init_secret_sync();
|
|
9664
|
-
import
|
|
9878
|
+
import crypto6 from "node:crypto";
|
|
9665
9879
|
function maskValue(value) {
|
|
9666
9880
|
if (value.length <= 4) return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
9667
9881
|
return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" + value.slice(-4);
|
|
@@ -9756,7 +9970,7 @@ async function secretsRoutes(app) {
|
|
|
9756
9970
|
const secrets = await vault.get("secrets") ?? [];
|
|
9757
9971
|
const resolvedScope = scope ?? (policyIds?.length > 0 ? "policed" : "global");
|
|
9758
9972
|
const newSecret = {
|
|
9759
|
-
id:
|
|
9973
|
+
id: crypto6.randomUUID(),
|
|
9760
9974
|
name: name.trim(),
|
|
9761
9975
|
value,
|
|
9762
9976
|
policyIds: resolvedScope === "standalone" ? [] : policyIds ?? [],
|
|
@@ -10057,7 +10271,7 @@ async function openclawRoutes(app) {
|
|
|
10057
10271
|
}
|
|
10058
10272
|
|
|
10059
10273
|
// libs/shield-daemon/src/routes/rpc.ts
|
|
10060
|
-
import * as
|
|
10274
|
+
import * as crypto7 from "node:crypto";
|
|
10061
10275
|
import * as nodefs from "node:fs";
|
|
10062
10276
|
|
|
10063
10277
|
// libs/shield-daemon/src/policy/url-matcher.ts
|
|
@@ -10171,7 +10385,7 @@ function checkUrlPolicy(policies, url) {
|
|
|
10171
10385
|
// libs/shield-daemon/src/proxy/server.ts
|
|
10172
10386
|
import * as http from "node:http";
|
|
10173
10387
|
import * as net from "node:net";
|
|
10174
|
-
function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
10388
|
+
function createPerRunProxy(urlPolicies, onActivity, logger, onBlock) {
|
|
10175
10389
|
const server = http.createServer((req, res) => {
|
|
10176
10390
|
onActivity();
|
|
10177
10391
|
const url = req.url;
|
|
@@ -10183,6 +10397,7 @@ function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
|
10183
10397
|
const allowed = checkUrlPolicy(urlPolicies, url);
|
|
10184
10398
|
if (!allowed) {
|
|
10185
10399
|
logger(`BLOCKED HTTP ${req.method} ${url}`);
|
|
10400
|
+
onBlock?.(req.method || "GET", url, "http");
|
|
10186
10401
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
10187
10402
|
res.end("Blocked by AgenShield URL policy");
|
|
10188
10403
|
return;
|
|
@@ -10226,6 +10441,7 @@ function createPerRunProxy(urlPolicies, onActivity, logger) {
|
|
|
10226
10441
|
const allowed = checkUrlPolicy(urlPolicies, `https://${hostname2}`);
|
|
10227
10442
|
if (!allowed) {
|
|
10228
10443
|
logger(`BLOCKED CONNECT ${hostname2}:${port}`);
|
|
10444
|
+
onBlock?.("CONNECT", `${hostname2}:${port}`, "https");
|
|
10229
10445
|
clientSocket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
|
|
10230
10446
|
clientSocket.destroy();
|
|
10231
10447
|
return;
|
|
@@ -10288,7 +10504,16 @@ var ProxyPool = class {
|
|
|
10288
10504
|
const logger = (msg) => {
|
|
10289
10505
|
console.log(`[proxy:${execId.slice(0, 8)}] ${msg}`);
|
|
10290
10506
|
};
|
|
10291
|
-
const
|
|
10507
|
+
const onBlock = (method, target, protocol) => {
|
|
10508
|
+
emitInterceptorEvent({
|
|
10509
|
+
type: "denied",
|
|
10510
|
+
operation: "http_request",
|
|
10511
|
+
target: protocol === "https" ? `https://${target}` : target,
|
|
10512
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10513
|
+
error: `Blocked by URL policy (${method})`
|
|
10514
|
+
});
|
|
10515
|
+
};
|
|
10516
|
+
const server = createPerRunProxy(urlPolicies, onActivity, logger, onBlock);
|
|
10292
10517
|
const port = await new Promise((resolve3, reject) => {
|
|
10293
10518
|
server.listen(0, "127.0.0.1", () => {
|
|
10294
10519
|
const addr = server.address();
|
|
@@ -10371,12 +10596,19 @@ function matchCommandPattern(pattern, target) {
|
|
|
10371
10596
|
normalizedTarget = firstSpace >= 0 ? basename6 + target.slice(firstSpace) : basename6;
|
|
10372
10597
|
}
|
|
10373
10598
|
if (trimmed.endsWith(":*")) {
|
|
10374
|
-
|
|
10599
|
+
let prefix = trimmed.slice(0, -2);
|
|
10600
|
+
if (prefix.includes("/")) {
|
|
10601
|
+
prefix = prefix.split("/").pop() || prefix;
|
|
10602
|
+
}
|
|
10375
10603
|
const lowerTarget = normalizedTarget.toLowerCase();
|
|
10376
10604
|
const lowerPrefix = prefix.toLowerCase();
|
|
10377
10605
|
return lowerTarget === lowerPrefix || lowerTarget.startsWith(lowerPrefix + " ");
|
|
10378
10606
|
}
|
|
10379
|
-
|
|
10607
|
+
let normalizedPattern = trimmed;
|
|
10608
|
+
if (trimmed.includes("/")) {
|
|
10609
|
+
normalizedPattern = trimmed.split("/").pop() || trimmed;
|
|
10610
|
+
}
|
|
10611
|
+
return normalizedTarget.toLowerCase() === normalizedPattern.toLowerCase();
|
|
10380
10612
|
}
|
|
10381
10613
|
function operationToTarget(operation) {
|
|
10382
10614
|
switch (operation) {
|
|
@@ -10469,7 +10701,7 @@ async function buildSandboxConfig(config, matchedPolicy, _context, target) {
|
|
|
10469
10701
|
console.log(`[sandbox] direct network access (no proxy)`);
|
|
10470
10702
|
sandbox.networkAllowed = true;
|
|
10471
10703
|
} else if (networkMode === "proxy") {
|
|
10472
|
-
const execId =
|
|
10704
|
+
const execId = crypto7.randomUUID();
|
|
10473
10705
|
const commandBasename = extractCommandBasename(target || "");
|
|
10474
10706
|
const urlPolicies = filterUrlPoliciesForCommand(config.policies || [], commandBasename);
|
|
10475
10707
|
const pool = getProxyPool();
|
|
@@ -10534,7 +10766,11 @@ async function evaluatePolicyCheck(operation, target, context) {
|
|
|
10534
10766
|
} else if (targetType === "command") {
|
|
10535
10767
|
matches = matchCommandPattern(pattern, effectiveTarget);
|
|
10536
10768
|
} else {
|
|
10537
|
-
|
|
10769
|
+
let fsPattern = pattern;
|
|
10770
|
+
if (targetType === "filesystem" && fsPattern.endsWith("/")) {
|
|
10771
|
+
fsPattern = fsPattern + "**";
|
|
10772
|
+
}
|
|
10773
|
+
const regex = globToRegex(fsPattern);
|
|
10538
10774
|
matches = regex.test(effectiveTarget);
|
|
10539
10775
|
}
|
|
10540
10776
|
console.log("[policy_check] pattern:", pattern, "-> base:", targetType === "url" ? normalizeUrlBase(pattern) : pattern, "| target:", effectiveTarget, "| matches:", matches);
|
|
@@ -10600,12 +10836,29 @@ async function handleHttpRequest(params) {
|
|
|
10600
10836
|
body: responseBody
|
|
10601
10837
|
};
|
|
10602
10838
|
}
|
|
10839
|
+
async function handlePolicyCheck(params) {
|
|
10840
|
+
const operation = String(params["operation"] ?? "");
|
|
10841
|
+
const target = String(params["target"] ?? "");
|
|
10842
|
+
const context = params["context"];
|
|
10843
|
+
const result = await evaluatePolicyCheck(operation, target, context);
|
|
10844
|
+
if (!result.allowed) {
|
|
10845
|
+
if (operation === "exec") {
|
|
10846
|
+
emitExecDenied(target, result.reason || "Denied by policy");
|
|
10847
|
+
} else {
|
|
10848
|
+
emitInterceptorEvent({
|
|
10849
|
+
type: "denied",
|
|
10850
|
+
operation,
|
|
10851
|
+
target,
|
|
10852
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10853
|
+
policyId: result.policyId,
|
|
10854
|
+
error: result.reason || "Denied by policy"
|
|
10855
|
+
});
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
return result;
|
|
10859
|
+
}
|
|
10603
10860
|
var handlers = {
|
|
10604
|
-
policy_check: (params) =>
|
|
10605
|
-
String(params["operation"] ?? ""),
|
|
10606
|
-
String(params["target"] ?? ""),
|
|
10607
|
-
params["context"]
|
|
10608
|
-
),
|
|
10861
|
+
policy_check: (params) => handlePolicyCheck(params),
|
|
10609
10862
|
events_batch: (params) => handleEventsBatch(params),
|
|
10610
10863
|
http_request: (params) => handleHttpRequest(params),
|
|
10611
10864
|
ping: () => ({ status: "ok" })
|
|
@@ -10830,6 +11083,12 @@ async function startServer(config) {
|
|
|
10830
11083
|
fs23.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
|
|
10831
11084
|
console.log(`[Daemon] Created skills directory: ${skillsDir2}`);
|
|
10832
11085
|
}
|
|
11086
|
+
try {
|
|
11087
|
+
await getInstallationKey();
|
|
11088
|
+
console.log("[Daemon] Installation key ready");
|
|
11089
|
+
} catch (err) {
|
|
11090
|
+
console.warn("[Daemon] Failed to initialize installation key:", err.message);
|
|
11091
|
+
}
|
|
10833
11092
|
startSkillsWatcher(skillsDir2, {
|
|
10834
11093
|
onUntrustedDetected: (info) => emitSkillUntrustedDetected(info.name, info.reason),
|
|
10835
11094
|
onApproved: (name) => emitSkillApproved(name)
|