@agenshield/daemon 0.5.0 → 0.6.0
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/acl.d.ts.map +1 -1
- package/command-sync.d.ts.map +1 -1
- package/index.js +433 -280
- package/main.js +437 -284
- package/package.json +4 -4
- package/policy-sync.d.ts +25 -0
- package/policy-sync.d.ts.map +1 -0
- package/routes/config.d.ts.map +1 -1
- package/ui-assets/assets/{index-C4yZ-JLI.js → index-DRQK9Oxg.js} +224 -224
- package/ui-assets/index.html +1 -1
package/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// libs/shield-daemon/src/main.ts
|
|
4
|
-
import * as
|
|
4
|
+
import * as fs21 from "node:fs";
|
|
5
5
|
|
|
6
6
|
// libs/shield-daemon/src/config/paths.ts
|
|
7
7
|
import * as path from "node:path";
|
|
@@ -86,7 +86,7 @@ function updateConfig(updates) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// libs/shield-daemon/src/server.ts
|
|
89
|
-
import * as
|
|
89
|
+
import * as fs20 from "node:fs";
|
|
90
90
|
import Fastify from "fastify";
|
|
91
91
|
import cors from "@fastify/cors";
|
|
92
92
|
import fastifyStatic from "@fastify/static";
|
|
@@ -213,8 +213,8 @@ async function statusRoutes(app) {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// libs/shield-daemon/src/routes/config.ts
|
|
216
|
-
import * as
|
|
217
|
-
import * as
|
|
216
|
+
import * as fs7 from "node:fs";
|
|
217
|
+
import * as path7 from "node:path";
|
|
218
218
|
|
|
219
219
|
// libs/shield-daemon/src/vault/index.ts
|
|
220
220
|
import * as fs3 from "node:fs";
|
|
@@ -518,8 +518,30 @@ function getSessionManager() {
|
|
|
518
518
|
import { execSync as execSync2 } from "node:child_process";
|
|
519
519
|
import * as os3 from "node:os";
|
|
520
520
|
import * as fs4 from "node:fs";
|
|
521
|
+
import * as path4 from "node:path";
|
|
521
522
|
var noop = { warn() {
|
|
522
523
|
} };
|
|
524
|
+
var TRAVERSAL_PERMS = "search";
|
|
525
|
+
var WORLD_TRAVERSABLE_PATHS = /* @__PURE__ */ new Set([
|
|
526
|
+
"/",
|
|
527
|
+
"/Users",
|
|
528
|
+
"/tmp",
|
|
529
|
+
"/private",
|
|
530
|
+
"/private/tmp",
|
|
531
|
+
"/private/var",
|
|
532
|
+
"/var",
|
|
533
|
+
"/opt",
|
|
534
|
+
"/usr",
|
|
535
|
+
"/usr/local",
|
|
536
|
+
"/Applications",
|
|
537
|
+
"/Library",
|
|
538
|
+
"/System",
|
|
539
|
+
"/Volumes"
|
|
540
|
+
]);
|
|
541
|
+
function normalizePath(p) {
|
|
542
|
+
if (p === "/") return p;
|
|
543
|
+
return p.replace(/\/+$/, "") || "/";
|
|
544
|
+
}
|
|
523
545
|
function stripGlobToBasePath(pattern) {
|
|
524
546
|
let p = pattern;
|
|
525
547
|
if (p.startsWith("~")) {
|
|
@@ -531,7 +553,7 @@ function stripGlobToBasePath(pattern) {
|
|
|
531
553
|
if (/[*?[]/.test(seg)) break;
|
|
532
554
|
base.push(seg);
|
|
533
555
|
}
|
|
534
|
-
return base.length === 0 ? "/" : base.join("/") || "/";
|
|
556
|
+
return normalizePath(base.length === 0 ? "/" : base.join("/") || "/");
|
|
535
557
|
}
|
|
536
558
|
function operationsToAclPerms(operations) {
|
|
537
559
|
const perms = [];
|
|
@@ -589,49 +611,69 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
|
|
|
589
611
|
log.warn(`[acl] failed to read ACLs on ${targetPath}: ${err.message}`);
|
|
590
612
|
}
|
|
591
613
|
}
|
|
592
|
-
function
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
for (const [id, oldP] of oldMap) {
|
|
599
|
-
if (!newMap.has(id)) {
|
|
600
|
-
for (const pattern of oldP.patterns) {
|
|
601
|
-
removeGroupAcl(stripGlobToBasePath(pattern), groupName, log);
|
|
602
|
-
}
|
|
614
|
+
function getAncestorsNeedingTraversal(targetPath) {
|
|
615
|
+
const ancestors = [];
|
|
616
|
+
let dir = path4.dirname(targetPath);
|
|
617
|
+
while (dir !== targetPath && dir !== "/") {
|
|
618
|
+
if (!WORLD_TRAVERSABLE_PATHS.has(dir)) {
|
|
619
|
+
ancestors.push(dir);
|
|
603
620
|
}
|
|
621
|
+
targetPath = dir;
|
|
622
|
+
dir = path4.dirname(dir);
|
|
604
623
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
624
|
+
return ancestors;
|
|
625
|
+
}
|
|
626
|
+
function mergePerms(a, b) {
|
|
627
|
+
const set = /* @__PURE__ */ new Set([
|
|
628
|
+
...a.split(",").filter(Boolean),
|
|
629
|
+
...b.split(",").filter(Boolean)
|
|
630
|
+
]);
|
|
631
|
+
return [...set].join(",");
|
|
632
|
+
}
|
|
633
|
+
function computeAclMap(policies) {
|
|
634
|
+
const aclMap = /* @__PURE__ */ new Map();
|
|
635
|
+
for (const policy of policies) {
|
|
636
|
+
const perms = operationsToAclPerms(policy.operations ?? []);
|
|
637
|
+
if (!perms) continue;
|
|
638
|
+
for (const pattern of policy.patterns) {
|
|
639
|
+
const target = stripGlobToBasePath(pattern);
|
|
640
|
+
const existing = aclMap.get(target);
|
|
641
|
+
aclMap.set(target, existing ? mergePerms(existing, perms) : perms);
|
|
612
642
|
}
|
|
613
643
|
}
|
|
614
|
-
for (const
|
|
615
|
-
const
|
|
616
|
-
if (!
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const perms = operationsToAclPerms(newP.operations ?? []);
|
|
624
|
-
if (!perms) continue;
|
|
625
|
-
for (const pattern of newP.patterns) {
|
|
626
|
-
addGroupAcl(stripGlobToBasePath(pattern), groupName, perms, log);
|
|
644
|
+
for (const policy of policies) {
|
|
645
|
+
const perms = operationsToAclPerms(policy.operations ?? []);
|
|
646
|
+
if (!perms) continue;
|
|
647
|
+
for (const pattern of policy.patterns) {
|
|
648
|
+
const target = stripGlobToBasePath(pattern);
|
|
649
|
+
for (const ancestor of getAncestorsNeedingTraversal(target)) {
|
|
650
|
+
if (!aclMap.has(ancestor)) {
|
|
651
|
+
aclMap.set(ancestor, TRAVERSAL_PERMS);
|
|
652
|
+
}
|
|
627
653
|
}
|
|
628
654
|
}
|
|
629
655
|
}
|
|
656
|
+
return aclMap;
|
|
657
|
+
}
|
|
658
|
+
function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
|
|
659
|
+
const log = logger ?? noop;
|
|
660
|
+
const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
|
|
661
|
+
const newFs = newPolicies.filter((p) => p.target === "filesystem");
|
|
662
|
+
const oldAclMap = computeAclMap(oldFs);
|
|
663
|
+
const newAclMap = computeAclMap(newFs);
|
|
664
|
+
for (const oldPath of oldAclMap.keys()) {
|
|
665
|
+
if (!newAclMap.has(oldPath)) {
|
|
666
|
+
removeGroupAcl(oldPath, groupName, log);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
for (const [targetPath, perms] of newAclMap) {
|
|
670
|
+
addGroupAcl(targetPath, groupName, perms, log);
|
|
671
|
+
}
|
|
630
672
|
}
|
|
631
673
|
|
|
632
674
|
// libs/shield-daemon/src/command-sync.ts
|
|
633
675
|
import * as fs5 from "node:fs";
|
|
634
|
-
import * as
|
|
676
|
+
import * as path5 from "node:path";
|
|
635
677
|
import { execSync as execSync3 } from "node:child_process";
|
|
636
678
|
var noop2 = { warn() {
|
|
637
679
|
}, info() {
|
|
@@ -664,7 +706,7 @@ var ALL_PROXIED_COMMANDS = [
|
|
|
664
706
|
function resolveCommandPaths(name) {
|
|
665
707
|
const paths = [];
|
|
666
708
|
for (const dir of BIN_SEARCH_DIRS) {
|
|
667
|
-
const candidate =
|
|
709
|
+
const candidate = path5.join(dir, name);
|
|
668
710
|
try {
|
|
669
711
|
if (fs5.existsSync(candidate)) {
|
|
670
712
|
const stat = fs5.statSync(candidate);
|
|
@@ -678,7 +720,7 @@ function resolveCommandPaths(name) {
|
|
|
678
720
|
if (paths.length === 0) {
|
|
679
721
|
try {
|
|
680
722
|
const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
681
|
-
if (result &&
|
|
723
|
+
if (result && path5.isAbsolute(result)) {
|
|
682
724
|
paths.push(result);
|
|
683
725
|
}
|
|
684
726
|
} catch {
|
|
@@ -720,15 +762,26 @@ function syncCommandPolicies(policies, logger) {
|
|
|
720
762
|
version: "1.0.0",
|
|
721
763
|
commands
|
|
722
764
|
};
|
|
765
|
+
const json = JSON.stringify(config, null, 2) + "\n";
|
|
723
766
|
try {
|
|
724
|
-
const dir =
|
|
767
|
+
const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
|
|
725
768
|
if (!fs5.existsSync(dir)) {
|
|
726
769
|
fs5.mkdirSync(dir, { recursive: true });
|
|
727
770
|
}
|
|
728
|
-
fs5.writeFileSync(ALLOWED_COMMANDS_PATH,
|
|
771
|
+
fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
|
|
729
772
|
log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
|
|
730
|
-
} catch
|
|
731
|
-
|
|
773
|
+
} catch {
|
|
774
|
+
try {
|
|
775
|
+
execSync3(`sudo -n tee "${ALLOWED_COMMANDS_PATH}" > /dev/null`, {
|
|
776
|
+
input: json,
|
|
777
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
778
|
+
timeout: 5e3
|
|
779
|
+
});
|
|
780
|
+
log.info(`[command-sync] wrote ${commands.length} commands to allowlist (via sudo)`);
|
|
781
|
+
} catch (sudoErr) {
|
|
782
|
+
log.warn(`[command-sync] cannot write to ${ALLOWED_COMMANDS_PATH} \u2014 fix with: sudo chmod 775 ${path5.dirname(ALLOWED_COMMANDS_PATH)}`);
|
|
783
|
+
log.warn(`[command-sync] error: ${sudoErr.message}`);
|
|
784
|
+
}
|
|
732
785
|
}
|
|
733
786
|
}
|
|
734
787
|
function installWrappersInDir(binDir, log) {
|
|
@@ -736,24 +789,38 @@ function installWrappersInDir(binDir, log) {
|
|
|
736
789
|
if (!fs5.existsSync(binDir)) {
|
|
737
790
|
try {
|
|
738
791
|
fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
739
|
-
} catch
|
|
740
|
-
|
|
741
|
-
|
|
792
|
+
} catch {
|
|
793
|
+
try {
|
|
794
|
+
execSync3(`sudo -n mkdir -p "${binDir}" && sudo -n chmod 755 "${binDir}"`, {
|
|
795
|
+
stdio: "pipe",
|
|
796
|
+
timeout: 5e3
|
|
797
|
+
});
|
|
798
|
+
} catch (sudoErr) {
|
|
799
|
+
log.warn(`[command-sync] failed to create bin dir ${binDir}: ${sudoErr.message}`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
742
802
|
}
|
|
743
803
|
}
|
|
804
|
+
if (!fs5.existsSync(shieldExecPath)) {
|
|
805
|
+
log.warn(`[command-sync] shield-exec not found at ${shieldExecPath}, skipping wrapper installation`);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
744
808
|
for (const cmd of ALL_PROXIED_COMMANDS) {
|
|
745
|
-
const wrapperPath =
|
|
809
|
+
const wrapperPath = path5.join(binDir, cmd);
|
|
746
810
|
if (fs5.existsSync(wrapperPath)) {
|
|
747
811
|
continue;
|
|
748
812
|
}
|
|
749
813
|
try {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
814
|
+
fs5.symlinkSync(shieldExecPath, wrapperPath);
|
|
815
|
+
} catch {
|
|
816
|
+
try {
|
|
817
|
+
execSync3(`sudo -n ln -s "${shieldExecPath}" "${wrapperPath}"`, {
|
|
818
|
+
stdio: "pipe",
|
|
819
|
+
timeout: 5e3
|
|
820
|
+
});
|
|
821
|
+
} catch (sudoErr) {
|
|
822
|
+
log.warn(`[command-sync] failed to create wrapper ${wrapperPath}: ${sudoErr.message}`);
|
|
754
823
|
}
|
|
755
|
-
} catch (err) {
|
|
756
|
-
log.warn(`[command-sync] failed to create wrapper ${wrapperPath}: ${err.message}`);
|
|
757
824
|
}
|
|
758
825
|
}
|
|
759
826
|
}
|
|
@@ -764,12 +831,12 @@ function ensureWrappersInstalled(state, logger) {
|
|
|
764
831
|
log.warn("[command-sync] no agent user in state, skipping wrapper installation");
|
|
765
832
|
return;
|
|
766
833
|
}
|
|
767
|
-
const agentBinDir =
|
|
834
|
+
const agentBinDir = path5.join(agentUser.homeDir, "bin");
|
|
768
835
|
log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
|
|
769
836
|
installWrappersInDir(agentBinDir, log);
|
|
770
837
|
const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
|
|
771
838
|
if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
|
|
772
|
-
const envBinDir =
|
|
839
|
+
const envBinDir = path5.join(agentHomeEnv, "bin");
|
|
773
840
|
log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
|
|
774
841
|
installWrappersInDir(envBinDir, log);
|
|
775
842
|
}
|
|
@@ -779,6 +846,90 @@ function syncCommandPoliciesAndWrappers(policies, state, logger) {
|
|
|
779
846
|
ensureWrappersInstalled(state, logger);
|
|
780
847
|
}
|
|
781
848
|
|
|
849
|
+
// libs/shield-daemon/src/policy-sync.ts
|
|
850
|
+
import * as fs6 from "node:fs";
|
|
851
|
+
import * as path6 from "node:path";
|
|
852
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
853
|
+
var noop3 = { warn() {
|
|
854
|
+
}, info() {
|
|
855
|
+
} };
|
|
856
|
+
var BROKER_POLICIES_DIR = "/opt/agenshield/policies";
|
|
857
|
+
var USER_POLICIES_FILE = path6.join(BROKER_POLICIES_DIR, "custom", "user-policies.json");
|
|
858
|
+
var BROKER_TARGETS = /* @__PURE__ */ new Set(["url", "command", "skill"]);
|
|
859
|
+
function normalizeUrlPatterns(pattern) {
|
|
860
|
+
let p = pattern.trim();
|
|
861
|
+
p = p.replace(/\/+$/, "");
|
|
862
|
+
if (!p.match(/^(\*|https?):\/\//i)) {
|
|
863
|
+
p = `https://${p}`;
|
|
864
|
+
}
|
|
865
|
+
if (p.endsWith("*")) {
|
|
866
|
+
const base = p.replace(/\/\*+$/, "");
|
|
867
|
+
if (base !== p) {
|
|
868
|
+
return [base, p];
|
|
869
|
+
}
|
|
870
|
+
return [p];
|
|
871
|
+
}
|
|
872
|
+
return [p, `${p}/**`];
|
|
873
|
+
}
|
|
874
|
+
function inferOperations(target) {
|
|
875
|
+
switch (target) {
|
|
876
|
+
case "url":
|
|
877
|
+
return ["http_request", "open_url"];
|
|
878
|
+
case "command":
|
|
879
|
+
return ["exec"];
|
|
880
|
+
case "skill":
|
|
881
|
+
return ["skill_install", "skill_uninstall"];
|
|
882
|
+
default:
|
|
883
|
+
return ["*"];
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function toBrokerRule(policy) {
|
|
887
|
+
let patterns;
|
|
888
|
+
if (policy.target === "url") {
|
|
889
|
+
patterns = policy.patterns.flatMap(normalizeUrlPatterns);
|
|
890
|
+
patterns = [...new Set(patterns)];
|
|
891
|
+
} else {
|
|
892
|
+
patterns = [...policy.patterns];
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
id: policy.id,
|
|
896
|
+
name: policy.name,
|
|
897
|
+
action: policy.action,
|
|
898
|
+
target: policy.target,
|
|
899
|
+
operations: policy.operations?.length ? [...policy.operations] : inferOperations(policy.target),
|
|
900
|
+
patterns,
|
|
901
|
+
enabled: policy.enabled,
|
|
902
|
+
priority: policy.priority ?? 0
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function syncPoliciesToBrokerDir(policies, logger) {
|
|
906
|
+
const log = logger ?? noop3;
|
|
907
|
+
const applicable = policies.filter((p) => BROKER_TARGETS.has(p.target));
|
|
908
|
+
const rules = applicable.map(toBrokerRule);
|
|
909
|
+
const payload = { rules };
|
|
910
|
+
const json = JSON.stringify(payload, null, 2) + "\n";
|
|
911
|
+
try {
|
|
912
|
+
const dir = path6.dirname(USER_POLICIES_FILE);
|
|
913
|
+
if (!fs6.existsSync(dir)) {
|
|
914
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
915
|
+
}
|
|
916
|
+
fs6.writeFileSync(USER_POLICIES_FILE, json, "utf-8");
|
|
917
|
+
log.info(`[policy-sync] wrote ${rules.length} user policies to broker dir`);
|
|
918
|
+
} catch {
|
|
919
|
+
try {
|
|
920
|
+
execSync4(`sudo -n tee "${USER_POLICIES_FILE}" > /dev/null`, {
|
|
921
|
+
input: json,
|
|
922
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
923
|
+
timeout: 5e3
|
|
924
|
+
});
|
|
925
|
+
log.info(`[policy-sync] wrote ${rules.length} user policies to broker dir (via sudo)`);
|
|
926
|
+
} catch (sudoErr) {
|
|
927
|
+
log.warn(`[policy-sync] cannot write to ${USER_POLICIES_FILE} \u2014 fix with: sudo chmod 775 ${path6.dirname(USER_POLICIES_FILE)}`);
|
|
928
|
+
log.warn(`[policy-sync] error: ${sudoErr.message}`);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
782
933
|
// libs/shield-daemon/src/routes/config.ts
|
|
783
934
|
async function configRoutes(app) {
|
|
784
935
|
app.get("/config", async () => {
|
|
@@ -801,6 +952,7 @@ async function configRoutes(app) {
|
|
|
801
952
|
syncFilesystemPolicyAcls(oldPolicies, updated.policies, wsGroup.name, app.log);
|
|
802
953
|
}
|
|
803
954
|
syncCommandPoliciesAndWrappers(updated.policies, state, app.log);
|
|
955
|
+
syncPoliciesToBrokerDir(updated.policies, app.log);
|
|
804
956
|
}
|
|
805
957
|
return {
|
|
806
958
|
success: true,
|
|
@@ -826,6 +978,7 @@ async function configRoutes(app) {
|
|
|
826
978
|
syncFilesystemPolicyAcls(oldConfig.policies, [], wsGroup.name, app.log);
|
|
827
979
|
}
|
|
828
980
|
syncCommandPoliciesAndWrappers([], state, app.log);
|
|
981
|
+
syncPoliciesToBrokerDir([], app.log);
|
|
829
982
|
saveConfig(getDefaultConfig());
|
|
830
983
|
const vault = getVault();
|
|
831
984
|
await vault.destroy();
|
|
@@ -843,7 +996,7 @@ async function configRoutes(app) {
|
|
|
843
996
|
});
|
|
844
997
|
app.get("/config/openclaw", async (_request, reply) => {
|
|
845
998
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
846
|
-
const configDir =
|
|
999
|
+
const configDir = path7.join(agentHome, ".openclaw");
|
|
847
1000
|
const configFiles = readConfigDir(configDir);
|
|
848
1001
|
return reply.send({ configDir, files: configFiles });
|
|
849
1002
|
});
|
|
@@ -855,7 +1008,7 @@ async function configRoutes(app) {
|
|
|
855
1008
|
return reply.code(400).send({ error: "original query param required" });
|
|
856
1009
|
}
|
|
857
1010
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
858
|
-
const agentConfigDir =
|
|
1011
|
+
const agentConfigDir = path7.join(agentHome, ".openclaw");
|
|
859
1012
|
const diff = diffConfigDirs(original, agentConfigDir);
|
|
860
1013
|
return reply.send({ diff });
|
|
861
1014
|
}
|
|
@@ -865,27 +1018,27 @@ var SKIP_DIRS = /* @__PURE__ */ new Set(["skills", "node_modules", ".git", "dist
|
|
|
865
1018
|
function readConfigDir(dir, base) {
|
|
866
1019
|
const result = {};
|
|
867
1020
|
const root = base ?? dir;
|
|
868
|
-
if (!
|
|
1021
|
+
if (!fs7.existsSync(dir)) {
|
|
869
1022
|
return result;
|
|
870
1023
|
}
|
|
871
1024
|
let entries;
|
|
872
1025
|
try {
|
|
873
|
-
entries =
|
|
1026
|
+
entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
874
1027
|
} catch {
|
|
875
1028
|
return result;
|
|
876
1029
|
}
|
|
877
1030
|
for (const entry of entries) {
|
|
878
1031
|
if (entry.isDirectory()) {
|
|
879
1032
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
880
|
-
const sub = readConfigDir(
|
|
1033
|
+
const sub = readConfigDir(path7.join(dir, entry.name), root);
|
|
881
1034
|
Object.assign(result, sub);
|
|
882
1035
|
} else if (entry.isFile()) {
|
|
883
|
-
const ext =
|
|
1036
|
+
const ext = path7.extname(entry.name).toLowerCase();
|
|
884
1037
|
if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
|
|
885
|
-
const filePath =
|
|
886
|
-
const relPath =
|
|
1038
|
+
const filePath = path7.join(dir, entry.name);
|
|
1039
|
+
const relPath = path7.relative(root, filePath);
|
|
887
1040
|
try {
|
|
888
|
-
result[relPath] =
|
|
1041
|
+
result[relPath] = fs7.readFileSync(filePath, "utf-8");
|
|
889
1042
|
} catch {
|
|
890
1043
|
}
|
|
891
1044
|
}
|
|
@@ -1005,10 +1158,10 @@ function emitSecurityWarning(warning) {
|
|
|
1005
1158
|
function emitSecurityCritical(issue) {
|
|
1006
1159
|
daemonEvents.broadcast("security:critical", { message: issue });
|
|
1007
1160
|
}
|
|
1008
|
-
function emitApiRequest(method,
|
|
1161
|
+
function emitApiRequest(method, path20, statusCode, duration, requestBody, responseBody) {
|
|
1009
1162
|
daemonEvents.broadcast("api:request", {
|
|
1010
1163
|
method,
|
|
1011
|
-
path:
|
|
1164
|
+
path: path20,
|
|
1012
1165
|
statusCode,
|
|
1013
1166
|
duration,
|
|
1014
1167
|
...requestBody !== void 0 && { requestBody },
|
|
@@ -1215,16 +1368,16 @@ var PROTECTED_ROUTES = [
|
|
|
1215
1368
|
{ method: "POST", path: "/api/config/factory-reset" },
|
|
1216
1369
|
{ method: "POST", path: "/api/skills/install" }
|
|
1217
1370
|
];
|
|
1218
|
-
function isProtectedRoute(method,
|
|
1371
|
+
function isProtectedRoute(method, path20) {
|
|
1219
1372
|
return PROTECTED_ROUTES.some(
|
|
1220
|
-
(route) => route.method === method &&
|
|
1373
|
+
(route) => route.method === method && path20.startsWith(route.path)
|
|
1221
1374
|
);
|
|
1222
1375
|
}
|
|
1223
1376
|
function createAuthHook() {
|
|
1224
1377
|
return async (request, reply) => {
|
|
1225
1378
|
const method = request.method;
|
|
1226
|
-
const
|
|
1227
|
-
if (!isProtectedRoute(method,
|
|
1379
|
+
const path20 = request.url.split("?")[0];
|
|
1380
|
+
if (!isProtectedRoute(method, path20)) {
|
|
1228
1381
|
return;
|
|
1229
1382
|
}
|
|
1230
1383
|
if (!isAuthenticated(request)) {
|
|
@@ -2056,8 +2209,8 @@ function getMCPState() {
|
|
|
2056
2209
|
}
|
|
2057
2210
|
|
|
2058
2211
|
// libs/shield-daemon/src/services/integration-skills.ts
|
|
2059
|
-
import * as
|
|
2060
|
-
import * as
|
|
2212
|
+
import * as fs9 from "node:fs";
|
|
2213
|
+
import * as path9 from "node:path";
|
|
2061
2214
|
|
|
2062
2215
|
// libs/shield-skills/dist/index.js
|
|
2063
2216
|
import * as path22 from "node:path";
|
|
@@ -2122,9 +2275,9 @@ var __skills_dirname = path22.dirname(fileURLToPath(import.meta.url));
|
|
|
2122
2275
|
var BUILTIN_SKILLS_DIR = path22.resolve(__skills_dirname, "..", "skills");
|
|
2123
2276
|
|
|
2124
2277
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
2125
|
-
import * as
|
|
2126
|
-
import * as
|
|
2127
|
-
import { execSync as
|
|
2278
|
+
import * as fs8 from "node:fs";
|
|
2279
|
+
import * as path8 from "node:path";
|
|
2280
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
2128
2281
|
var APPROVED_SKILLS_PATH = "/opt/agenshield/config/approved-skills.json";
|
|
2129
2282
|
var QUARANTINE_DIR = "/opt/agenshield/quarantine/skills";
|
|
2130
2283
|
var DEBOUNCE_MS = 500;
|
|
@@ -2135,8 +2288,8 @@ var skillsDir = "";
|
|
|
2135
2288
|
var callbacks = {};
|
|
2136
2289
|
function loadApprovedSkills() {
|
|
2137
2290
|
try {
|
|
2138
|
-
if (
|
|
2139
|
-
const content =
|
|
2291
|
+
if (fs8.existsSync(APPROVED_SKILLS_PATH)) {
|
|
2292
|
+
const content = fs8.readFileSync(APPROVED_SKILLS_PATH, "utf-8");
|
|
2140
2293
|
return JSON.parse(content);
|
|
2141
2294
|
}
|
|
2142
2295
|
} catch {
|
|
@@ -2145,10 +2298,10 @@ function loadApprovedSkills() {
|
|
|
2145
2298
|
}
|
|
2146
2299
|
function saveApprovedSkills(skills) {
|
|
2147
2300
|
try {
|
|
2148
|
-
const dir =
|
|
2149
|
-
|
|
2301
|
+
const dir = path8.dirname(APPROVED_SKILLS_PATH);
|
|
2302
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
2150
2303
|
const content = JSON.stringify(skills, null, 2);
|
|
2151
|
-
|
|
2304
|
+
fs8.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
|
|
2152
2305
|
} catch (err) {
|
|
2153
2306
|
console.error("Failed to save approved skills:", err.message);
|
|
2154
2307
|
}
|
|
@@ -2159,16 +2312,16 @@ function isApproved(skillName) {
|
|
|
2159
2312
|
}
|
|
2160
2313
|
function quarantineSkill(skillName, skillPath) {
|
|
2161
2314
|
try {
|
|
2162
|
-
const quarantinePath =
|
|
2163
|
-
if (!
|
|
2164
|
-
|
|
2315
|
+
const quarantinePath = path8.join(QUARANTINE_DIR, skillName);
|
|
2316
|
+
if (!fs8.existsSync(QUARANTINE_DIR)) {
|
|
2317
|
+
fs8.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
|
|
2165
2318
|
}
|
|
2166
|
-
if (
|
|
2167
|
-
|
|
2319
|
+
if (fs8.existsSync(quarantinePath)) {
|
|
2320
|
+
fs8.rmSync(quarantinePath, { recursive: true, force: true });
|
|
2168
2321
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2322
|
+
execSync5(`mv "${skillPath}" "${quarantinePath}"`, { stdio: "pipe" });
|
|
2323
|
+
execSync5(`chown -R root:wheel "${quarantinePath}"`, { stdio: "pipe" });
|
|
2324
|
+
execSync5(`chmod -R 700 "${quarantinePath}"`, { stdio: "pipe" });
|
|
2172
2325
|
const info = {
|
|
2173
2326
|
name: skillName,
|
|
2174
2327
|
quarantinedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2183,16 +2336,16 @@ function quarantineSkill(skillName, skillPath) {
|
|
|
2183
2336
|
}
|
|
2184
2337
|
}
|
|
2185
2338
|
function scanSkills() {
|
|
2186
|
-
if (!skillsDir || !
|
|
2339
|
+
if (!skillsDir || !fs8.existsSync(skillsDir)) {
|
|
2187
2340
|
return;
|
|
2188
2341
|
}
|
|
2189
2342
|
try {
|
|
2190
|
-
const entries =
|
|
2343
|
+
const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
|
|
2191
2344
|
for (const entry of entries) {
|
|
2192
2345
|
if (!entry.isDirectory()) continue;
|
|
2193
2346
|
const skillName = entry.name;
|
|
2194
2347
|
if (!isApproved(skillName)) {
|
|
2195
|
-
const fullPath =
|
|
2348
|
+
const fullPath = path8.join(skillsDir, skillName);
|
|
2196
2349
|
const info = quarantineSkill(skillName, fullPath);
|
|
2197
2350
|
if (info && callbacks.onQuarantined) {
|
|
2198
2351
|
callbacks.onQuarantined(info);
|
|
@@ -2225,8 +2378,8 @@ function startSkillsWatcher(watchDir, cbs = {}, pollIntervalMs = 3e4) {
|
|
|
2225
2378
|
callbacks = cbs;
|
|
2226
2379
|
scanSkills();
|
|
2227
2380
|
try {
|
|
2228
|
-
if (
|
|
2229
|
-
watcher =
|
|
2381
|
+
if (fs8.existsSync(skillsDir)) {
|
|
2382
|
+
watcher = fs8.watch(skillsDir, { persistent: false }, handleFsEvent);
|
|
2230
2383
|
watcher.on("error", (err) => {
|
|
2231
2384
|
console.warn("[SkillsWatcher] fs.watch error, falling back to polling:", err.message);
|
|
2232
2385
|
watcher?.close();
|
|
@@ -2258,9 +2411,9 @@ function stopSkillsWatcher() {
|
|
|
2258
2411
|
}
|
|
2259
2412
|
function approveSkill(skillName) {
|
|
2260
2413
|
try {
|
|
2261
|
-
const quarantinedPath =
|
|
2262
|
-
const destPath =
|
|
2263
|
-
if (!
|
|
2414
|
+
const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
|
|
2415
|
+
const destPath = path8.join(skillsDir, skillName);
|
|
2416
|
+
if (!fs8.existsSync(quarantinedPath)) {
|
|
2264
2417
|
return { success: false, error: `Skill "${skillName}" not found in quarantine` };
|
|
2265
2418
|
}
|
|
2266
2419
|
const approved = loadApprovedSkills();
|
|
@@ -2271,9 +2424,9 @@ function approveSkill(skillName) {
|
|
|
2271
2424
|
});
|
|
2272
2425
|
saveApprovedSkills(approved);
|
|
2273
2426
|
}
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2427
|
+
execSync5(`mv "${quarantinedPath}" "${destPath}"`, { stdio: "pipe" });
|
|
2428
|
+
execSync5(`chown -R root:wheel "${destPath}"`, { stdio: "pipe" });
|
|
2429
|
+
execSync5(`chmod -R a+rX,go-w "${destPath}"`, { stdio: "pipe" });
|
|
2277
2430
|
if (callbacks.onApproved) {
|
|
2278
2431
|
callbacks.onApproved(skillName);
|
|
2279
2432
|
}
|
|
@@ -2285,11 +2438,11 @@ function approveSkill(skillName) {
|
|
|
2285
2438
|
}
|
|
2286
2439
|
function rejectSkill(skillName) {
|
|
2287
2440
|
try {
|
|
2288
|
-
const quarantinedPath =
|
|
2289
|
-
if (!
|
|
2441
|
+
const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
|
|
2442
|
+
if (!fs8.existsSync(quarantinedPath)) {
|
|
2290
2443
|
return { success: false, error: `Skill "${skillName}" not found in quarantine` };
|
|
2291
2444
|
}
|
|
2292
|
-
|
|
2445
|
+
fs8.rmSync(quarantinedPath, { recursive: true, force: true });
|
|
2293
2446
|
console.log(`[SkillsWatcher] Rejected and deleted skill: ${skillName}`);
|
|
2294
2447
|
return { success: true };
|
|
2295
2448
|
} catch (err) {
|
|
@@ -2301,8 +2454,8 @@ function revokeSkill(skillName) {
|
|
|
2301
2454
|
const approved = loadApprovedSkills();
|
|
2302
2455
|
const filtered = approved.filter((s) => s.name !== skillName);
|
|
2303
2456
|
saveApprovedSkills(filtered);
|
|
2304
|
-
const skillPath =
|
|
2305
|
-
if (
|
|
2457
|
+
const skillPath = path8.join(skillsDir, skillName);
|
|
2458
|
+
if (fs8.existsSync(skillPath)) {
|
|
2306
2459
|
quarantineSkill(skillName, skillPath);
|
|
2307
2460
|
}
|
|
2308
2461
|
console.log(`[SkillsWatcher] Revoked approval for skill: ${skillName}`);
|
|
@@ -2314,17 +2467,17 @@ function revokeSkill(skillName) {
|
|
|
2314
2467
|
function listQuarantined() {
|
|
2315
2468
|
const results = [];
|
|
2316
2469
|
try {
|
|
2317
|
-
if (!
|
|
2470
|
+
if (!fs8.existsSync(QUARANTINE_DIR)) {
|
|
2318
2471
|
return results;
|
|
2319
2472
|
}
|
|
2320
|
-
const entries =
|
|
2473
|
+
const entries = fs8.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
|
|
2321
2474
|
for (const entry of entries) {
|
|
2322
2475
|
if (entry.isDirectory()) {
|
|
2323
|
-
const stat =
|
|
2476
|
+
const stat = fs8.statSync(path8.join(QUARANTINE_DIR, entry.name));
|
|
2324
2477
|
results.push({
|
|
2325
2478
|
name: entry.name,
|
|
2326
2479
|
quarantinedAt: stat.mtime.toISOString(),
|
|
2327
|
-
originalPath:
|
|
2480
|
+
originalPath: path8.join(skillsDir, entry.name),
|
|
2328
2481
|
reason: "Skill not in approved list"
|
|
2329
2482
|
});
|
|
2330
2483
|
}
|
|
@@ -2361,15 +2514,15 @@ function removeFromApprovedList(skillName) {
|
|
|
2361
2514
|
// libs/shield-daemon/src/services/integration-skills.ts
|
|
2362
2515
|
var AGENCO_SKILL_NAME = "agenco-secure-integrations";
|
|
2363
2516
|
function copyDirSync(src, dest) {
|
|
2364
|
-
|
|
2365
|
-
const entries =
|
|
2517
|
+
fs9.mkdirSync(dest, { recursive: true });
|
|
2518
|
+
const entries = fs9.readdirSync(src, { withFileTypes: true });
|
|
2366
2519
|
for (const entry of entries) {
|
|
2367
|
-
const srcPath =
|
|
2368
|
-
const destPath =
|
|
2520
|
+
const srcPath = path9.join(src, entry.name);
|
|
2521
|
+
const destPath = path9.join(dest, entry.name);
|
|
2369
2522
|
if (entry.isDirectory()) {
|
|
2370
2523
|
copyDirSync(srcPath, destPath);
|
|
2371
2524
|
} else {
|
|
2372
|
-
|
|
2525
|
+
fs9.copyFileSync(srcPath, destPath);
|
|
2373
2526
|
}
|
|
2374
2527
|
}
|
|
2375
2528
|
}
|
|
@@ -2379,9 +2532,9 @@ async function provisionAgenCoSkill() {
|
|
|
2379
2532
|
console.warn("[IntegrationSkills] Skills directory not configured \u2014 skipping provision");
|
|
2380
2533
|
return { installed: false };
|
|
2381
2534
|
}
|
|
2382
|
-
const destDir =
|
|
2383
|
-
const srcDir =
|
|
2384
|
-
if (
|
|
2535
|
+
const destDir = path9.join(skillsDir2, AGENCO_SKILL_NAME);
|
|
2536
|
+
const srcDir = path9.join(BUILTIN_SKILLS_DIR, AGENCO_SKILL_NAME);
|
|
2537
|
+
if (fs9.existsSync(destDir)) {
|
|
2385
2538
|
return { installed: false };
|
|
2386
2539
|
}
|
|
2387
2540
|
try {
|
|
@@ -2392,8 +2545,8 @@ async function provisionAgenCoSkill() {
|
|
|
2392
2545
|
} catch (err) {
|
|
2393
2546
|
console.error(`[IntegrationSkills] Failed to install "${AGENCO_SKILL_NAME}":`, err.message);
|
|
2394
2547
|
try {
|
|
2395
|
-
if (
|
|
2396
|
-
|
|
2548
|
+
if (fs9.existsSync(destDir)) {
|
|
2549
|
+
fs9.rmSync(destDir, { recursive: true, force: true });
|
|
2397
2550
|
}
|
|
2398
2551
|
removeFromApprovedList(AGENCO_SKILL_NAME);
|
|
2399
2552
|
} catch {
|
|
@@ -2408,12 +2561,12 @@ async function provisionIntegrationSkill(integrationSlug) {
|
|
|
2408
2561
|
return { installed: false };
|
|
2409
2562
|
}
|
|
2410
2563
|
const skillName = `integration-${integrationSlug}`;
|
|
2411
|
-
const destDir =
|
|
2412
|
-
const srcDir =
|
|
2413
|
-
if (
|
|
2564
|
+
const destDir = path9.join(skillsDir2, skillName);
|
|
2565
|
+
const srcDir = path9.join(BUILTIN_SKILLS_DIR, skillName);
|
|
2566
|
+
if (fs9.existsSync(destDir)) {
|
|
2414
2567
|
return { installed: false };
|
|
2415
2568
|
}
|
|
2416
|
-
if (!
|
|
2569
|
+
if (!fs9.existsSync(srcDir)) {
|
|
2417
2570
|
return { installed: false };
|
|
2418
2571
|
}
|
|
2419
2572
|
try {
|
|
@@ -2424,8 +2577,8 @@ async function provisionIntegrationSkill(integrationSlug) {
|
|
|
2424
2577
|
} catch (err) {
|
|
2425
2578
|
console.error(`[IntegrationSkills] Failed to install "${skillName}":`, err.message);
|
|
2426
2579
|
try {
|
|
2427
|
-
if (
|
|
2428
|
-
|
|
2580
|
+
if (fs9.existsSync(destDir)) {
|
|
2581
|
+
fs9.rmSync(destDir, { recursive: true, force: true });
|
|
2429
2582
|
}
|
|
2430
2583
|
removeFromApprovedList(skillName);
|
|
2431
2584
|
} catch {
|
|
@@ -4948,15 +5101,15 @@ async function agencoRoutes(app) {
|
|
|
4948
5101
|
}
|
|
4949
5102
|
|
|
4950
5103
|
// libs/shield-daemon/src/routes/skills.ts
|
|
4951
|
-
import * as
|
|
4952
|
-
import * as
|
|
4953
|
-
import { execSync as
|
|
5104
|
+
import * as fs14 from "node:fs";
|
|
5105
|
+
import * as path14 from "node:path";
|
|
5106
|
+
import { execSync as execSync9 } from "node:child_process";
|
|
4954
5107
|
import { parseSkillMd } from "@agenshield/sandbox";
|
|
4955
5108
|
|
|
4956
5109
|
// libs/shield-daemon/src/services/skill-analyzer.ts
|
|
4957
|
-
import * as
|
|
4958
|
-
import * as
|
|
4959
|
-
import { execSync as
|
|
5110
|
+
import * as fs10 from "node:fs";
|
|
5111
|
+
import * as path10 from "node:path";
|
|
5112
|
+
import { execSync as execSync6 } from "node:child_process";
|
|
4960
5113
|
var ANALYSIS_CACHE_PATH = "/opt/agenshield/config/skill-analyses.json";
|
|
4961
5114
|
var COMMAND_PATTERNS = [
|
|
4962
5115
|
// Bash-style: command at start of line or after pipe/semicolon
|
|
@@ -5017,8 +5170,8 @@ var IGNORE_WORDS = /* @__PURE__ */ new Set([
|
|
|
5017
5170
|
]);
|
|
5018
5171
|
function loadCache() {
|
|
5019
5172
|
try {
|
|
5020
|
-
if (
|
|
5021
|
-
return JSON.parse(
|
|
5173
|
+
if (fs10.existsSync(ANALYSIS_CACHE_PATH)) {
|
|
5174
|
+
return JSON.parse(fs10.readFileSync(ANALYSIS_CACHE_PATH, "utf-8"));
|
|
5022
5175
|
}
|
|
5023
5176
|
} catch {
|
|
5024
5177
|
}
|
|
@@ -5026,18 +5179,18 @@ function loadCache() {
|
|
|
5026
5179
|
}
|
|
5027
5180
|
function saveCache(cache3) {
|
|
5028
5181
|
try {
|
|
5029
|
-
const dir =
|
|
5030
|
-
if (!
|
|
5031
|
-
|
|
5182
|
+
const dir = path10.dirname(ANALYSIS_CACHE_PATH);
|
|
5183
|
+
if (!fs10.existsSync(dir)) {
|
|
5184
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
5032
5185
|
}
|
|
5033
|
-
|
|
5186
|
+
fs10.writeFileSync(ANALYSIS_CACHE_PATH, JSON.stringify(cache3, null, 2) + "\n", "utf-8");
|
|
5034
5187
|
} catch (err) {
|
|
5035
5188
|
console.error("[SkillAnalyzer] Failed to save cache:", err.message);
|
|
5036
5189
|
}
|
|
5037
5190
|
}
|
|
5038
5191
|
function resolveCommand(name) {
|
|
5039
5192
|
try {
|
|
5040
|
-
const result =
|
|
5193
|
+
const result = execSync6(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
5041
5194
|
return result || void 0;
|
|
5042
5195
|
} catch {
|
|
5043
5196
|
return void 0;
|
|
@@ -5172,33 +5325,33 @@ function clearCachedAnalysis(skillName) {
|
|
|
5172
5325
|
}
|
|
5173
5326
|
|
|
5174
5327
|
// libs/shield-daemon/src/services/skill-lifecycle.ts
|
|
5175
|
-
import * as
|
|
5176
|
-
import * as
|
|
5177
|
-
import { execSync as
|
|
5328
|
+
import * as fs11 from "node:fs";
|
|
5329
|
+
import * as path11 from "node:path";
|
|
5330
|
+
import { execSync as execSync7 } from "node:child_process";
|
|
5178
5331
|
function createSkillWrapper(name, binDir) {
|
|
5179
|
-
if (!
|
|
5180
|
-
|
|
5332
|
+
if (!fs11.existsSync(binDir)) {
|
|
5333
|
+
fs11.mkdirSync(binDir, { recursive: true });
|
|
5181
5334
|
}
|
|
5182
|
-
const wrapperPath =
|
|
5335
|
+
const wrapperPath = path11.join(binDir, name);
|
|
5183
5336
|
const wrapperContent = `#!/bin/bash
|
|
5184
5337
|
# ${name} skill wrapper - policy-enforced execution
|
|
5185
5338
|
# Ensure accessible working directory
|
|
5186
5339
|
if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi
|
|
5187
5340
|
exec /opt/agenshield/bin/shield-client skill run "${name}" "$@"
|
|
5188
5341
|
`;
|
|
5189
|
-
|
|
5342
|
+
fs11.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
|
|
5190
5343
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5191
5344
|
try {
|
|
5192
|
-
|
|
5193
|
-
|
|
5345
|
+
execSync7(`chown root:${socketGroup} "${wrapperPath}"`, { stdio: "pipe" });
|
|
5346
|
+
execSync7(`chmod 755 "${wrapperPath}"`, { stdio: "pipe" });
|
|
5194
5347
|
} catch {
|
|
5195
5348
|
}
|
|
5196
5349
|
}
|
|
5197
5350
|
function removeSkillWrapper(name, binDir) {
|
|
5198
|
-
const wrapperPath =
|
|
5351
|
+
const wrapperPath = path11.join(binDir, name);
|
|
5199
5352
|
try {
|
|
5200
|
-
if (
|
|
5201
|
-
|
|
5353
|
+
if (fs11.existsSync(wrapperPath)) {
|
|
5354
|
+
fs11.unlinkSync(wrapperPath);
|
|
5202
5355
|
}
|
|
5203
5356
|
} catch {
|
|
5204
5357
|
}
|
|
@@ -5231,18 +5384,18 @@ function removeSkillPolicy(name) {
|
|
|
5231
5384
|
}
|
|
5232
5385
|
|
|
5233
5386
|
// libs/shield-daemon/src/services/openclaw-config.ts
|
|
5234
|
-
import * as
|
|
5235
|
-
import * as
|
|
5236
|
-
import { execSync as
|
|
5387
|
+
import * as fs12 from "node:fs";
|
|
5388
|
+
import * as path12 from "node:path";
|
|
5389
|
+
import { execSync as execSync8 } from "node:child_process";
|
|
5237
5390
|
function getOpenClawConfigPath() {
|
|
5238
5391
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5239
|
-
return
|
|
5392
|
+
return path12.join(agentHome, ".openclaw", "openclaw.json");
|
|
5240
5393
|
}
|
|
5241
5394
|
function readConfig() {
|
|
5242
5395
|
const configPath = getOpenClawConfigPath();
|
|
5243
5396
|
try {
|
|
5244
|
-
if (
|
|
5245
|
-
return JSON.parse(
|
|
5397
|
+
if (fs12.existsSync(configPath)) {
|
|
5398
|
+
return JSON.parse(fs12.readFileSync(configPath, "utf-8"));
|
|
5246
5399
|
}
|
|
5247
5400
|
} catch {
|
|
5248
5401
|
console.warn("[OpenClawConfig] Failed to read openclaw.json, starting fresh");
|
|
@@ -5251,14 +5404,14 @@ function readConfig() {
|
|
|
5251
5404
|
}
|
|
5252
5405
|
function writeConfig(config) {
|
|
5253
5406
|
const configPath = getOpenClawConfigPath();
|
|
5254
|
-
|
|
5255
|
-
|
|
5407
|
+
fs12.mkdirSync(path12.dirname(configPath), { recursive: true });
|
|
5408
|
+
fs12.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5256
5409
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5257
|
-
const brokerUser =
|
|
5410
|
+
const brokerUser = path12.basename(agentHome) + "_broker";
|
|
5258
5411
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5259
5412
|
try {
|
|
5260
|
-
|
|
5261
|
-
|
|
5413
|
+
execSync8(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
|
|
5414
|
+
execSync8(`chmod 664 "${configPath}"`, { stdio: "pipe" });
|
|
5262
5415
|
} catch {
|
|
5263
5416
|
}
|
|
5264
5417
|
}
|
|
@@ -5287,8 +5440,8 @@ function removeSkillEntry(slug) {
|
|
|
5287
5440
|
}
|
|
5288
5441
|
|
|
5289
5442
|
// libs/shield-daemon/src/services/marketplace.ts
|
|
5290
|
-
import * as
|
|
5291
|
-
import * as
|
|
5443
|
+
import * as fs13 from "node:fs";
|
|
5444
|
+
import * as path13 from "node:path";
|
|
5292
5445
|
import * as os4 from "node:os";
|
|
5293
5446
|
import { CONFIG_DIR as CONFIG_DIR2, MARKETPLACE_DIR } from "@agenshield/ipc";
|
|
5294
5447
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -5312,35 +5465,35 @@ var ANALYSIS_TIMEOUT = 4 * 6e4;
|
|
|
5312
5465
|
var SEARCH_CACHE_TTL = 6e4;
|
|
5313
5466
|
var DETAIL_CACHE_TTL = 5 * 6e4;
|
|
5314
5467
|
var SHORT_TIMEOUT = 1e4;
|
|
5315
|
-
async function convexAction(
|
|
5468
|
+
async function convexAction(path20, args, timeout) {
|
|
5316
5469
|
const res = await fetch(`${CONVEX_BASE}/api/action`, {
|
|
5317
5470
|
method: "POST",
|
|
5318
5471
|
signal: AbortSignal.timeout(timeout),
|
|
5319
5472
|
headers: { "Content-Type": "application/json" },
|
|
5320
|
-
body: JSON.stringify({ path:
|
|
5473
|
+
body: JSON.stringify({ path: path20, args, format: "json" })
|
|
5321
5474
|
});
|
|
5322
5475
|
if (!res.ok) {
|
|
5323
|
-
throw new Error(`Convex action ${
|
|
5476
|
+
throw new Error(`Convex action ${path20} returned ${res.status}`);
|
|
5324
5477
|
}
|
|
5325
5478
|
const body = await res.json();
|
|
5326
5479
|
if (body.status === "error") {
|
|
5327
|
-
throw new Error(`Convex action ${
|
|
5480
|
+
throw new Error(`Convex action ${path20}: ${body.errorMessage ?? "unknown error"}`);
|
|
5328
5481
|
}
|
|
5329
5482
|
return body.value;
|
|
5330
5483
|
}
|
|
5331
|
-
async function convexQuery(
|
|
5484
|
+
async function convexQuery(path20, args, timeout) {
|
|
5332
5485
|
const res = await fetch(`${CONVEX_BASE}/api/query`, {
|
|
5333
5486
|
method: "POST",
|
|
5334
5487
|
signal: AbortSignal.timeout(timeout),
|
|
5335
5488
|
headers: { "Content-Type": "application/json" },
|
|
5336
|
-
body: JSON.stringify({ path:
|
|
5489
|
+
body: JSON.stringify({ path: path20, args, format: "json" })
|
|
5337
5490
|
});
|
|
5338
5491
|
if (!res.ok) {
|
|
5339
|
-
throw new Error(`Convex query ${
|
|
5492
|
+
throw new Error(`Convex query ${path20} returned ${res.status}`);
|
|
5340
5493
|
}
|
|
5341
5494
|
const body = await res.json();
|
|
5342
5495
|
if (body.status === "error") {
|
|
5343
|
-
throw new Error(`Convex query ${
|
|
5496
|
+
throw new Error(`Convex query ${path20}: ${body.errorMessage ?? "unknown error"}`);
|
|
5344
5497
|
}
|
|
5345
5498
|
return body.value;
|
|
5346
5499
|
}
|
|
@@ -5401,7 +5554,7 @@ function isImageExt(filePath) {
|
|
|
5401
5554
|
}
|
|
5402
5555
|
var MAX_IMAGE_SIZE = 5e5;
|
|
5403
5556
|
function getMarketplaceDir() {
|
|
5404
|
-
return
|
|
5557
|
+
return path13.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
|
|
5405
5558
|
}
|
|
5406
5559
|
async function downloadAndExtractZip(slug) {
|
|
5407
5560
|
const url = `${CLAWHUB_DOWNLOAD_BASE}/download?slug=${encodeURIComponent(slug)}`;
|
|
@@ -5439,39 +5592,39 @@ async function downloadAndExtractZip(slug) {
|
|
|
5439
5592
|
return files;
|
|
5440
5593
|
}
|
|
5441
5594
|
function storeDownloadedSkill(slug, meta, files) {
|
|
5442
|
-
const dir =
|
|
5443
|
-
const filesDir =
|
|
5444
|
-
|
|
5595
|
+
const dir = path13.join(getMarketplaceDir(), slug);
|
|
5596
|
+
const filesDir = path13.join(dir, "files");
|
|
5597
|
+
fs13.mkdirSync(filesDir, { recursive: true });
|
|
5445
5598
|
const fullMeta = { ...meta, downloadedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5446
|
-
|
|
5599
|
+
fs13.writeFileSync(path13.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
|
|
5447
5600
|
for (const file of files) {
|
|
5448
|
-
const filePath =
|
|
5449
|
-
|
|
5450
|
-
|
|
5601
|
+
const filePath = path13.join(filesDir, file.name);
|
|
5602
|
+
fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
|
|
5603
|
+
fs13.writeFileSync(filePath, file.content, "utf-8");
|
|
5451
5604
|
}
|
|
5452
5605
|
console.log(`[Marketplace] Stored ${files.length} files for ${slug}`);
|
|
5453
5606
|
}
|
|
5454
5607
|
function updateDownloadedAnalysis(slug, analysis) {
|
|
5455
|
-
const metaPath =
|
|
5608
|
+
const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5456
5609
|
try {
|
|
5457
|
-
if (!
|
|
5458
|
-
const meta = JSON.parse(
|
|
5610
|
+
if (!fs13.existsSync(metaPath)) return;
|
|
5611
|
+
const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
|
|
5459
5612
|
meta.analysis = analysis;
|
|
5460
|
-
|
|
5613
|
+
fs13.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
5461
5614
|
} catch {
|
|
5462
5615
|
}
|
|
5463
5616
|
}
|
|
5464
5617
|
function listDownloadedSkills() {
|
|
5465
5618
|
const baseDir = getMarketplaceDir();
|
|
5466
|
-
if (!
|
|
5619
|
+
if (!fs13.existsSync(baseDir)) return [];
|
|
5467
5620
|
const results = [];
|
|
5468
5621
|
try {
|
|
5469
|
-
const entries =
|
|
5622
|
+
const entries = fs13.readdirSync(baseDir, { withFileTypes: true });
|
|
5470
5623
|
for (const entry of entries) {
|
|
5471
5624
|
if (!entry.isDirectory()) continue;
|
|
5472
|
-
const metaPath =
|
|
5625
|
+
const metaPath = path13.join(baseDir, entry.name, "metadata.json");
|
|
5473
5626
|
try {
|
|
5474
|
-
const meta = JSON.parse(
|
|
5627
|
+
const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
|
|
5475
5628
|
results.push({
|
|
5476
5629
|
slug: meta.slug,
|
|
5477
5630
|
name: meta.name,
|
|
@@ -5488,17 +5641,17 @@ function listDownloadedSkills() {
|
|
|
5488
5641
|
return results;
|
|
5489
5642
|
}
|
|
5490
5643
|
function getDownloadedSkillFiles(slug) {
|
|
5491
|
-
const filesDir =
|
|
5492
|
-
if (!
|
|
5644
|
+
const filesDir = path13.join(getMarketplaceDir(), slug, "files");
|
|
5645
|
+
if (!fs13.existsSync(filesDir)) return [];
|
|
5493
5646
|
const files = [];
|
|
5494
5647
|
function walk(dir, prefix) {
|
|
5495
|
-
const entries =
|
|
5648
|
+
const entries = fs13.readdirSync(dir, { withFileTypes: true });
|
|
5496
5649
|
for (const entry of entries) {
|
|
5497
5650
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
5498
5651
|
if (entry.isDirectory()) {
|
|
5499
|
-
walk(
|
|
5652
|
+
walk(path13.join(dir, entry.name), rel);
|
|
5500
5653
|
} else {
|
|
5501
|
-
const content =
|
|
5654
|
+
const content = fs13.readFileSync(path13.join(dir, entry.name), "utf-8");
|
|
5502
5655
|
files.push({ name: rel, type: guessContentType(entry.name), content });
|
|
5503
5656
|
}
|
|
5504
5657
|
}
|
|
@@ -5507,10 +5660,10 @@ function getDownloadedSkillFiles(slug) {
|
|
|
5507
5660
|
return files;
|
|
5508
5661
|
}
|
|
5509
5662
|
function getDownloadedSkillMeta(slug) {
|
|
5510
|
-
const metaPath =
|
|
5663
|
+
const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5511
5664
|
try {
|
|
5512
|
-
if (
|
|
5513
|
-
return JSON.parse(
|
|
5665
|
+
if (fs13.existsSync(metaPath)) {
|
|
5666
|
+
return JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
|
|
5514
5667
|
}
|
|
5515
5668
|
} catch {
|
|
5516
5669
|
}
|
|
@@ -6047,13 +6200,13 @@ function findSkillMdRecursive(dir, depth = 0) {
|
|
|
6047
6200
|
if (depth > 3) return null;
|
|
6048
6201
|
try {
|
|
6049
6202
|
for (const name of ["SKILL.md", "skill.md", "README.md", "readme.md"]) {
|
|
6050
|
-
const candidate =
|
|
6051
|
-
if (
|
|
6203
|
+
const candidate = path14.join(dir, name);
|
|
6204
|
+
if (fs14.existsSync(candidate)) return candidate;
|
|
6052
6205
|
}
|
|
6053
|
-
const entries =
|
|
6206
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
6054
6207
|
for (const entry of entries) {
|
|
6055
6208
|
if (!entry.isDirectory()) continue;
|
|
6056
|
-
const found = findSkillMdRecursive(
|
|
6209
|
+
const found = findSkillMdRecursive(path14.join(dir, entry.name), depth + 1);
|
|
6057
6210
|
if (found) return found;
|
|
6058
6211
|
}
|
|
6059
6212
|
} catch {
|
|
@@ -6064,7 +6217,7 @@ function readSkillDescription(skillDir) {
|
|
|
6064
6217
|
try {
|
|
6065
6218
|
const mdPath = findSkillMdRecursive(skillDir);
|
|
6066
6219
|
if (!mdPath) return void 0;
|
|
6067
|
-
const content =
|
|
6220
|
+
const content = fs14.readFileSync(mdPath, "utf-8");
|
|
6068
6221
|
const parsed = parseSkillMd(content);
|
|
6069
6222
|
return parsed?.metadata?.description ?? void 0;
|
|
6070
6223
|
} catch {
|
|
@@ -6083,7 +6236,7 @@ async function skillsRoutes(app) {
|
|
|
6083
6236
|
let onDiskNames = [];
|
|
6084
6237
|
if (skillsDir2) {
|
|
6085
6238
|
try {
|
|
6086
|
-
onDiskNames =
|
|
6239
|
+
onDiskNames = fs14.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
6087
6240
|
} catch {
|
|
6088
6241
|
}
|
|
6089
6242
|
}
|
|
@@ -6096,9 +6249,9 @@ async function skillsRoutes(app) {
|
|
|
6096
6249
|
name: a.name,
|
|
6097
6250
|
source: "user",
|
|
6098
6251
|
status: "active",
|
|
6099
|
-
path:
|
|
6252
|
+
path: path14.join(skillsDir2 ?? "", a.name),
|
|
6100
6253
|
publisher: a.publisher,
|
|
6101
|
-
description: skillsDir2 ? readSkillDescription(
|
|
6254
|
+
description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, a.name)) : void 0
|
|
6102
6255
|
})),
|
|
6103
6256
|
// Quarantined
|
|
6104
6257
|
...quarantined.map((q) => ({
|
|
@@ -6113,8 +6266,8 @@ async function skillsRoutes(app) {
|
|
|
6113
6266
|
name,
|
|
6114
6267
|
source: "workspace",
|
|
6115
6268
|
status: "workspace",
|
|
6116
|
-
path:
|
|
6117
|
-
description: skillsDir2 ? readSkillDescription(
|
|
6269
|
+
path: path14.join(skillsDir2 ?? "", name),
|
|
6270
|
+
description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, name)) : void 0
|
|
6118
6271
|
})),
|
|
6119
6272
|
// Downloaded (not installed) → available
|
|
6120
6273
|
...availableDownloads.map((d) => ({
|
|
@@ -6155,20 +6308,20 @@ async function skillsRoutes(app) {
|
|
|
6155
6308
|
} else if (entry) {
|
|
6156
6309
|
source = "user";
|
|
6157
6310
|
status = "active";
|
|
6158
|
-
skillPath = skillsDir2 ?
|
|
6311
|
+
skillPath = skillsDir2 ? path14.join(skillsDir2, name) : "";
|
|
6159
6312
|
} else if (skillsDir2) {
|
|
6160
6313
|
source = "workspace";
|
|
6161
6314
|
status = "workspace";
|
|
6162
|
-
skillPath =
|
|
6315
|
+
skillPath = path14.join(skillsDir2, name);
|
|
6163
6316
|
}
|
|
6164
6317
|
let content = "";
|
|
6165
6318
|
let metadata;
|
|
6166
|
-
const dirToRead = skillPath || (skillsDir2 ?
|
|
6319
|
+
const dirToRead = skillPath || (skillsDir2 ? path14.join(skillsDir2, name) : "");
|
|
6167
6320
|
if (dirToRead) {
|
|
6168
6321
|
try {
|
|
6169
6322
|
const mdPath = findSkillMdRecursive(dirToRead);
|
|
6170
6323
|
if (mdPath) {
|
|
6171
|
-
content =
|
|
6324
|
+
content = fs14.readFileSync(mdPath, "utf-8");
|
|
6172
6325
|
const parsed = parseSkillMd(content);
|
|
6173
6326
|
metadata = parsed?.metadata;
|
|
6174
6327
|
}
|
|
@@ -6297,10 +6450,10 @@ async function skillsRoutes(app) {
|
|
|
6297
6450
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
6298
6451
|
}
|
|
6299
6452
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
6300
|
-
const binDir =
|
|
6453
|
+
const binDir = path14.join(agentHome, "bin");
|
|
6301
6454
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6302
|
-
const skillDir =
|
|
6303
|
-
const isInstalled =
|
|
6455
|
+
const skillDir = path14.join(skillsDir2, name);
|
|
6456
|
+
const isInstalled = fs14.existsSync(skillDir);
|
|
6304
6457
|
if (isInstalled) {
|
|
6305
6458
|
try {
|
|
6306
6459
|
const brokerAvailable = await isBrokerAvailable();
|
|
@@ -6310,7 +6463,7 @@ async function skillsRoutes(app) {
|
|
|
6310
6463
|
agentHome
|
|
6311
6464
|
});
|
|
6312
6465
|
} else {
|
|
6313
|
-
|
|
6466
|
+
fs14.rmSync(skillDir, { recursive: true, force: true });
|
|
6314
6467
|
removeSkillWrapper(name, binDir);
|
|
6315
6468
|
}
|
|
6316
6469
|
removeSkillEntry(name);
|
|
@@ -6344,15 +6497,15 @@ async function skillsRoutes(app) {
|
|
|
6344
6497
|
throw new Error("Broker failed to install skill files");
|
|
6345
6498
|
}
|
|
6346
6499
|
} else {
|
|
6347
|
-
|
|
6500
|
+
fs14.mkdirSync(skillDir, { recursive: true });
|
|
6348
6501
|
for (const file of files) {
|
|
6349
|
-
const filePath =
|
|
6350
|
-
|
|
6351
|
-
|
|
6502
|
+
const filePath = path14.join(skillDir, file.name);
|
|
6503
|
+
fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
6504
|
+
fs14.writeFileSync(filePath, file.content, "utf-8");
|
|
6352
6505
|
}
|
|
6353
6506
|
try {
|
|
6354
|
-
|
|
6355
|
-
|
|
6507
|
+
execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
6508
|
+
execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
|
|
6356
6509
|
} catch {
|
|
6357
6510
|
}
|
|
6358
6511
|
createSkillWrapper(name, binDir);
|
|
@@ -6363,8 +6516,8 @@ async function skillsRoutes(app) {
|
|
|
6363
6516
|
return reply.send({ success: true, action: "enabled", name });
|
|
6364
6517
|
} catch (err) {
|
|
6365
6518
|
try {
|
|
6366
|
-
if (
|
|
6367
|
-
|
|
6519
|
+
if (fs14.existsSync(skillDir)) {
|
|
6520
|
+
fs14.rmSync(skillDir, { recursive: true, force: true });
|
|
6368
6521
|
}
|
|
6369
6522
|
removeFromApprovedList(name);
|
|
6370
6523
|
} catch {
|
|
@@ -6402,20 +6555,20 @@ async function skillsRoutes(app) {
|
|
|
6402
6555
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
6403
6556
|
}
|
|
6404
6557
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
6405
|
-
const binDir =
|
|
6558
|
+
const binDir = path14.join(agentHome, "bin");
|
|
6406
6559
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6407
|
-
const skillDir =
|
|
6560
|
+
const skillDir = path14.join(skillsDir2, name);
|
|
6408
6561
|
try {
|
|
6409
6562
|
addToApprovedList(name, publisher);
|
|
6410
|
-
|
|
6563
|
+
fs14.mkdirSync(skillDir, { recursive: true });
|
|
6411
6564
|
for (const file of files) {
|
|
6412
|
-
const filePath =
|
|
6413
|
-
|
|
6414
|
-
|
|
6565
|
+
const filePath = path14.join(skillDir, file.name);
|
|
6566
|
+
fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
6567
|
+
fs14.writeFileSync(filePath, file.content, "utf-8");
|
|
6415
6568
|
}
|
|
6416
6569
|
try {
|
|
6417
|
-
|
|
6418
|
-
|
|
6570
|
+
execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
|
|
6571
|
+
execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
|
|
6419
6572
|
} catch {
|
|
6420
6573
|
}
|
|
6421
6574
|
createSkillWrapper(name, binDir);
|
|
@@ -6423,8 +6576,8 @@ async function skillsRoutes(app) {
|
|
|
6423
6576
|
return reply.send({ success: true, name, analysis });
|
|
6424
6577
|
} catch (err) {
|
|
6425
6578
|
try {
|
|
6426
|
-
if (
|
|
6427
|
-
|
|
6579
|
+
if (fs14.existsSync(skillDir)) {
|
|
6580
|
+
fs14.rmSync(skillDir, { recursive: true, force: true });
|
|
6428
6581
|
}
|
|
6429
6582
|
removeFromApprovedList(name);
|
|
6430
6583
|
} catch {
|
|
@@ -6439,8 +6592,8 @@ async function skillsRoutes(app) {
|
|
|
6439
6592
|
}
|
|
6440
6593
|
|
|
6441
6594
|
// libs/shield-daemon/src/routes/exec.ts
|
|
6442
|
-
import * as
|
|
6443
|
-
import * as
|
|
6595
|
+
import * as fs15 from "node:fs";
|
|
6596
|
+
import * as path15 from "node:path";
|
|
6444
6597
|
var ALLOWED_COMMANDS_PATH2 = "/opt/agenshield/config/allowed-commands.json";
|
|
6445
6598
|
var BIN_DIRS = [
|
|
6446
6599
|
"/usr/bin",
|
|
@@ -6453,22 +6606,22 @@ var binCache = null;
|
|
|
6453
6606
|
var BIN_CACHE_TTL = 6e4;
|
|
6454
6607
|
var VALID_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
6455
6608
|
function loadConfig2() {
|
|
6456
|
-
if (!
|
|
6609
|
+
if (!fs15.existsSync(ALLOWED_COMMANDS_PATH2)) {
|
|
6457
6610
|
return { version: "1.0.0", commands: [] };
|
|
6458
6611
|
}
|
|
6459
6612
|
try {
|
|
6460
|
-
const content =
|
|
6613
|
+
const content = fs15.readFileSync(ALLOWED_COMMANDS_PATH2, "utf-8");
|
|
6461
6614
|
return JSON.parse(content);
|
|
6462
6615
|
} catch {
|
|
6463
6616
|
return { version: "1.0.0", commands: [] };
|
|
6464
6617
|
}
|
|
6465
6618
|
}
|
|
6466
6619
|
function saveConfig2(config) {
|
|
6467
|
-
const dir =
|
|
6468
|
-
if (!
|
|
6469
|
-
|
|
6620
|
+
const dir = path15.dirname(ALLOWED_COMMANDS_PATH2);
|
|
6621
|
+
if (!fs15.existsSync(dir)) {
|
|
6622
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
6470
6623
|
}
|
|
6471
|
-
|
|
6624
|
+
fs15.writeFileSync(ALLOWED_COMMANDS_PATH2, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
6472
6625
|
}
|
|
6473
6626
|
function scanSystemBins() {
|
|
6474
6627
|
const pathDirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
@@ -6477,13 +6630,13 @@ function scanSystemBins() {
|
|
|
6477
6630
|
const results = [];
|
|
6478
6631
|
for (const dir of allDirs) {
|
|
6479
6632
|
try {
|
|
6480
|
-
if (!
|
|
6481
|
-
const entries =
|
|
6633
|
+
if (!fs15.existsSync(dir)) continue;
|
|
6634
|
+
const entries = fs15.readdirSync(dir);
|
|
6482
6635
|
for (const entry of entries) {
|
|
6483
6636
|
if (seen.has(entry)) continue;
|
|
6484
|
-
const fullPath =
|
|
6637
|
+
const fullPath = path15.join(dir, entry);
|
|
6485
6638
|
try {
|
|
6486
|
-
const stat =
|
|
6639
|
+
const stat = fs15.statSync(fullPath);
|
|
6487
6640
|
if (stat.isFile() && (stat.mode & 73) !== 0) {
|
|
6488
6641
|
seen.add(entry);
|
|
6489
6642
|
results.push({ name: entry, path: fullPath });
|
|
@@ -6536,7 +6689,7 @@ async function execRoutes(app) {
|
|
|
6536
6689
|
};
|
|
6537
6690
|
}
|
|
6538
6691
|
for (const p of paths) {
|
|
6539
|
-
if (!
|
|
6692
|
+
if (!path15.isAbsolute(p)) {
|
|
6540
6693
|
return {
|
|
6541
6694
|
success: false,
|
|
6542
6695
|
error: {
|
|
@@ -6963,8 +7116,8 @@ async function secretsRoutes(app) {
|
|
|
6963
7116
|
}
|
|
6964
7117
|
|
|
6965
7118
|
// libs/shield-daemon/src/routes/marketplace.ts
|
|
6966
|
-
import * as
|
|
6967
|
-
import * as
|
|
7119
|
+
import * as fs16 from "node:fs";
|
|
7120
|
+
import * as path16 from "node:path";
|
|
6968
7121
|
async function marketplaceRoutes(app) {
|
|
6969
7122
|
app.get(
|
|
6970
7123
|
"/marketplace/search",
|
|
@@ -7160,9 +7313,9 @@ async function marketplaceRoutes(app) {
|
|
|
7160
7313
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
7161
7314
|
}
|
|
7162
7315
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
7163
|
-
const binDir =
|
|
7316
|
+
const binDir = path16.join(agentHome, "bin");
|
|
7164
7317
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
7165
|
-
skillDir =
|
|
7318
|
+
skillDir = path16.join(skillsDir2, slug);
|
|
7166
7319
|
emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
|
|
7167
7320
|
addToApprovedList(slug, publisher);
|
|
7168
7321
|
logs.push("Skill pre-approved");
|
|
@@ -7205,8 +7358,8 @@ async function marketplaceRoutes(app) {
|
|
|
7205
7358
|
});
|
|
7206
7359
|
} catch (err) {
|
|
7207
7360
|
try {
|
|
7208
|
-
if (skillDir &&
|
|
7209
|
-
|
|
7361
|
+
if (skillDir && fs16.existsSync(skillDir)) {
|
|
7362
|
+
fs16.rmSync(skillDir, { recursive: true, force: true });
|
|
7210
7363
|
}
|
|
7211
7364
|
removeFromApprovedList(slug);
|
|
7212
7365
|
} catch {
|
|
@@ -7229,18 +7382,18 @@ async function marketplaceRoutes(app) {
|
|
|
7229
7382
|
}
|
|
7230
7383
|
|
|
7231
7384
|
// libs/shield-daemon/src/routes/fs.ts
|
|
7232
|
-
import * as
|
|
7233
|
-
import * as
|
|
7385
|
+
import * as fs17 from "node:fs";
|
|
7386
|
+
import * as path17 from "node:path";
|
|
7234
7387
|
import * as os5 from "node:os";
|
|
7235
7388
|
var MAX_ENTRIES = 200;
|
|
7236
7389
|
async function fsRoutes(app) {
|
|
7237
7390
|
app.get("/fs/browse", async (request) => {
|
|
7238
7391
|
const dirPath = request.query.path || os5.homedir();
|
|
7239
7392
|
const showHidden = request.query.showHidden === "true";
|
|
7240
|
-
const resolvedPath =
|
|
7393
|
+
const resolvedPath = path17.resolve(dirPath);
|
|
7241
7394
|
let dirents;
|
|
7242
7395
|
try {
|
|
7243
|
-
dirents =
|
|
7396
|
+
dirents = fs17.readdirSync(resolvedPath, { withFileTypes: true });
|
|
7244
7397
|
} catch {
|
|
7245
7398
|
return { success: true, data: { entries: [] } };
|
|
7246
7399
|
}
|
|
@@ -7249,7 +7402,7 @@ async function fsRoutes(app) {
|
|
|
7249
7402
|
if (!showHidden && dirent.name.startsWith(".")) continue;
|
|
7250
7403
|
entries.push({
|
|
7251
7404
|
name: dirent.name,
|
|
7252
|
-
path:
|
|
7405
|
+
path: path17.join(resolvedPath, dirent.name),
|
|
7253
7406
|
type: dirent.isDirectory() ? "directory" : "file"
|
|
7254
7407
|
});
|
|
7255
7408
|
if (entries.length >= MAX_ENTRIES) break;
|
|
@@ -7263,8 +7416,8 @@ async function fsRoutes(app) {
|
|
|
7263
7416
|
}
|
|
7264
7417
|
|
|
7265
7418
|
// libs/shield-daemon/src/services/activity-log.ts
|
|
7266
|
-
import * as
|
|
7267
|
-
import * as
|
|
7419
|
+
import * as fs18 from "node:fs";
|
|
7420
|
+
import * as path18 from "node:path";
|
|
7268
7421
|
var ACTIVITY_FILE = "activity.jsonl";
|
|
7269
7422
|
var MAX_SIZE_BYTES = 100 * 1024 * 1024;
|
|
7270
7423
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -7281,12 +7434,12 @@ var ActivityLog = class {
|
|
|
7281
7434
|
writeCount = 0;
|
|
7282
7435
|
unsubscribe;
|
|
7283
7436
|
constructor() {
|
|
7284
|
-
this.filePath =
|
|
7437
|
+
this.filePath = path18.join(getConfigDir(), ACTIVITY_FILE);
|
|
7285
7438
|
}
|
|
7286
7439
|
/** Read historical events from the JSONL file, newest first */
|
|
7287
7440
|
getHistory(limit = 500) {
|
|
7288
|
-
if (!
|
|
7289
|
-
const content =
|
|
7441
|
+
if (!fs18.existsSync(this.filePath)) return [];
|
|
7442
|
+
const content = fs18.readFileSync(this.filePath, "utf-8");
|
|
7290
7443
|
const lines = content.split("\n").filter(Boolean);
|
|
7291
7444
|
const events = [];
|
|
7292
7445
|
for (const line of lines) {
|
|
@@ -7311,7 +7464,7 @@ var ActivityLog = class {
|
|
|
7311
7464
|
}
|
|
7312
7465
|
append(event) {
|
|
7313
7466
|
const line = JSON.stringify(event) + "\n";
|
|
7314
|
-
|
|
7467
|
+
fs18.appendFileSync(this.filePath, line, "utf-8");
|
|
7315
7468
|
this.writeCount++;
|
|
7316
7469
|
if (this.writeCount % PRUNE_INTERVAL === 0) {
|
|
7317
7470
|
this.rotate();
|
|
@@ -7319,7 +7472,7 @@ var ActivityLog = class {
|
|
|
7319
7472
|
}
|
|
7320
7473
|
rotate() {
|
|
7321
7474
|
try {
|
|
7322
|
-
const stat =
|
|
7475
|
+
const stat = fs18.statSync(this.filePath);
|
|
7323
7476
|
if (stat.size > MAX_SIZE_BYTES) {
|
|
7324
7477
|
this.truncateBySize();
|
|
7325
7478
|
}
|
|
@@ -7329,15 +7482,15 @@ var ActivityLog = class {
|
|
|
7329
7482
|
}
|
|
7330
7483
|
/** Keep newest half of lines when file exceeds size limit */
|
|
7331
7484
|
truncateBySize() {
|
|
7332
|
-
const content =
|
|
7485
|
+
const content = fs18.readFileSync(this.filePath, "utf-8");
|
|
7333
7486
|
const lines = content.split("\n").filter(Boolean);
|
|
7334
7487
|
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
7335
|
-
|
|
7488
|
+
fs18.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
|
|
7336
7489
|
}
|
|
7337
7490
|
/** Remove entries older than 24 hours */
|
|
7338
7491
|
pruneOldEntries() {
|
|
7339
|
-
if (!
|
|
7340
|
-
const content =
|
|
7492
|
+
if (!fs18.existsSync(this.filePath)) return;
|
|
7493
|
+
const content = fs18.readFileSync(this.filePath, "utf-8");
|
|
7341
7494
|
const lines = content.split("\n").filter(Boolean);
|
|
7342
7495
|
const cutoff = Date.now() - MAX_AGE_MS;
|
|
7343
7496
|
const kept = lines.filter((line) => {
|
|
@@ -7349,7 +7502,7 @@ var ActivityLog = class {
|
|
|
7349
7502
|
}
|
|
7350
7503
|
});
|
|
7351
7504
|
if (kept.length < lines.length) {
|
|
7352
|
-
|
|
7505
|
+
fs18.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
|
|
7353
7506
|
}
|
|
7354
7507
|
}
|
|
7355
7508
|
};
|
|
@@ -7396,11 +7549,11 @@ function normalizeUrlTarget(url) {
|
|
|
7396
7549
|
const trimmed = url.trim();
|
|
7397
7550
|
try {
|
|
7398
7551
|
const parsed = new URL(trimmed);
|
|
7399
|
-
let
|
|
7400
|
-
if (
|
|
7401
|
-
|
|
7552
|
+
let path20 = parsed.pathname;
|
|
7553
|
+
if (path20.length > 1) {
|
|
7554
|
+
path20 = path20.replace(/\/+$/, "");
|
|
7402
7555
|
}
|
|
7403
|
-
return `${parsed.protocol}//${parsed.host}${
|
|
7556
|
+
return `${parsed.protocol}//${parsed.host}${path20}${parsed.search}`;
|
|
7404
7557
|
} catch {
|
|
7405
7558
|
return trimmed.replace(/\/+$/, "");
|
|
7406
7559
|
}
|
|
@@ -7634,22 +7787,22 @@ async function registerRoutes(app) {
|
|
|
7634
7787
|
}
|
|
7635
7788
|
|
|
7636
7789
|
// libs/shield-daemon/src/static.ts
|
|
7637
|
-
import * as
|
|
7638
|
-
import * as
|
|
7790
|
+
import * as fs19 from "node:fs";
|
|
7791
|
+
import * as path19 from "node:path";
|
|
7639
7792
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7640
7793
|
var __filename = fileURLToPath2(import.meta.url);
|
|
7641
|
-
var __dirname =
|
|
7794
|
+
var __dirname = path19.dirname(__filename);
|
|
7642
7795
|
function getUiAssetsPath() {
|
|
7643
|
-
const pkgRootPath =
|
|
7644
|
-
if (
|
|
7796
|
+
const pkgRootPath = path19.join(__dirname, "..", "ui-assets");
|
|
7797
|
+
if (fs19.existsSync(pkgRootPath)) {
|
|
7645
7798
|
return pkgRootPath;
|
|
7646
7799
|
}
|
|
7647
|
-
const bundledPath =
|
|
7648
|
-
if (
|
|
7800
|
+
const bundledPath = path19.join(__dirname, "ui-assets");
|
|
7801
|
+
if (fs19.existsSync(bundledPath)) {
|
|
7649
7802
|
return bundledPath;
|
|
7650
7803
|
}
|
|
7651
|
-
const devPath =
|
|
7652
|
-
if (
|
|
7804
|
+
const devPath = path19.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
|
|
7805
|
+
if (fs19.existsSync(devPath)) {
|
|
7653
7806
|
return devPath;
|
|
7654
7807
|
}
|
|
7655
7808
|
return null;
|
|
@@ -7738,8 +7891,8 @@ async function startServer(config) {
|
|
|
7738
7891
|
startSecurityWatcher(1e4);
|
|
7739
7892
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
7740
7893
|
const skillsDir2 = `${agentHome}/.openclaw/skills`;
|
|
7741
|
-
if (!
|
|
7742
|
-
|
|
7894
|
+
if (!fs20.existsSync(skillsDir2)) {
|
|
7895
|
+
fs20.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
|
|
7743
7896
|
console.log(`[Daemon] Created skills directory: ${skillsDir2}`);
|
|
7744
7897
|
}
|
|
7745
7898
|
startSkillsWatcher(skillsDir2, {
|
|
@@ -7775,10 +7928,10 @@ async function main() {
|
|
|
7775
7928
|
ensureConfigDir();
|
|
7776
7929
|
const config = loadConfig();
|
|
7777
7930
|
const pidPath = getPidPath();
|
|
7778
|
-
|
|
7931
|
+
fs21.writeFileSync(pidPath, process.pid.toString(), "utf-8");
|
|
7779
7932
|
const cleanup = () => {
|
|
7780
|
-
if (
|
|
7781
|
-
|
|
7933
|
+
if (fs21.existsSync(pidPath)) {
|
|
7934
|
+
fs21.unlinkSync(pidPath);
|
|
7782
7935
|
}
|
|
7783
7936
|
process.exit(0);
|
|
7784
7937
|
};
|