@agenshield/daemon 0.6.0 → 0.6.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 CHANGED
@@ -1,25 +1,12 @@
1
- // libs/shield-daemon/src/server.ts
2
- import * as fs20 from "node:fs";
3
- import Fastify from "fastify";
4
- import cors from "@fastify/cors";
5
- import fastifyStatic from "@fastify/static";
6
-
7
- // libs/shield-daemon/src/routes/index.ts
8
- import { API_PREFIX } from "@agenshield/ipc";
9
-
10
- // libs/shield-daemon/src/routes/health.ts
11
- async function healthRoutes(app) {
12
- app.get("/health", async () => {
13
- return {
14
- success: true,
15
- data: {
16
- ok: true,
17
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
18
- mode: "daemon"
19
- }
20
- };
21
- });
22
- }
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
23
10
 
24
11
  // libs/shield-daemon/src/config/paths.ts
25
12
  import * as path from "node:path";
@@ -37,10 +24,14 @@ function getPidPath() {
37
24
  function getLogPath() {
38
25
  return path.join(getConfigDir(), LOG_FILE);
39
26
  }
27
+ var init_paths = __esm({
28
+ "libs/shield-daemon/src/config/paths.ts"() {
29
+ "use strict";
30
+ }
31
+ });
40
32
 
41
33
  // libs/shield-daemon/src/config/defaults.ts
42
34
  import { DEFAULT_PORT, DEFAULT_HOST } from "@agenshield/ipc";
43
- var VERSION = "0.1.0";
44
35
  function getDefaultConfig() {
45
36
  return {
46
37
  version: VERSION,
@@ -57,8 +48,22 @@ function getDefaultConfig() {
57
48
  }
58
49
  };
59
50
  }
51
+ var VERSION;
52
+ var init_defaults = __esm({
53
+ "libs/shield-daemon/src/config/defaults.ts"() {
54
+ "use strict";
55
+ VERSION = "0.1.0";
56
+ }
57
+ });
60
58
 
61
59
  // libs/shield-daemon/src/config/loader.ts
60
+ var loader_exports = {};
61
+ __export(loader_exports, {
62
+ ensureConfigDir: () => ensureConfigDir,
63
+ loadConfig: () => loadConfig,
64
+ saveConfig: () => saveConfig,
65
+ updateConfig: () => updateConfig
66
+ });
62
67
  import * as fs from "node:fs";
63
68
  import { ShieldConfigSchema, DEFAULT_PORT as DEFAULT_PORT2 } from "@agenshield/ipc";
64
69
  function ensureConfigDir() {
@@ -105,8 +110,35 @@ function updateConfig(updates) {
105
110
  saveConfig(updated);
106
111
  return updated;
107
112
  }
113
+ var init_loader = __esm({
114
+ "libs/shield-daemon/src/config/loader.ts"() {
115
+ "use strict";
116
+ init_paths();
117
+ init_defaults();
118
+ }
119
+ });
108
120
 
109
121
  // libs/shield-daemon/src/state/index.ts
122
+ var state_exports = {};
123
+ __export(state_exports, {
124
+ addConnectedIntegration: () => addConnectedIntegration,
125
+ addGroupState: () => addGroupState,
126
+ addUserState: () => addUserState,
127
+ getDefaultState: () => getDefaultState,
128
+ getPasscodeProtectionState: () => getPasscodeProtectionState,
129
+ getStatePath: () => getStatePath,
130
+ initializeState: () => initializeState,
131
+ loadState: () => loadState,
132
+ removeConnectedIntegration: () => removeConnectedIntegration,
133
+ removeGroupState: () => removeGroupState,
134
+ removeUserState: () => removeUserState,
135
+ saveState: () => saveState,
136
+ updateAgenCoState: () => updateAgenCoState,
137
+ updateDaemonState: () => updateDaemonState,
138
+ updateInstallationState: () => updateInstallationState,
139
+ updatePasscodeProtectionState: () => updatePasscodeProtectionState,
140
+ updateState: () => updateState
141
+ });
110
142
  import * as fs2 from "node:fs";
111
143
  import * as path2 from "node:path";
112
144
  import { STATE_FILE, DEFAULT_PORT as DEFAULT_PORT3 } from "@agenshield/ipc";
@@ -184,12 +216,24 @@ function updateState(updates) {
184
216
  saveState(updated);
185
217
  return updated;
186
218
  }
219
+ function updateDaemonState(updates) {
220
+ const current = loadState();
221
+ current.daemon = { ...current.daemon, ...updates };
222
+ saveState(current);
223
+ return current;
224
+ }
187
225
  function updateAgenCoState(updates) {
188
226
  const current = loadState();
189
227
  current.agenco = { ...current.agenco, ...updates };
190
228
  saveState(current);
191
229
  return current;
192
230
  }
231
+ function updateInstallationState(updates) {
232
+ const current = loadState();
233
+ current.installation = { ...current.installation, ...updates };
234
+ saveState(current);
235
+ return current;
236
+ }
193
237
  function updatePasscodeProtectionState(updates) {
194
238
  const current = loadState();
195
239
  current.passcodeProtection = { ...current.passcodeProtection, ...updates };
@@ -200,6 +244,40 @@ function getPasscodeProtectionState() {
200
244
  const current = loadState();
201
245
  return current.passcodeProtection;
202
246
  }
247
+ function addUserState(user) {
248
+ const current = loadState();
249
+ const existingIndex = current.users.findIndex((u) => u.username === user.username);
250
+ if (existingIndex >= 0) {
251
+ current.users[existingIndex] = user;
252
+ } else {
253
+ current.users.push(user);
254
+ }
255
+ saveState(current);
256
+ return current;
257
+ }
258
+ function removeUserState(username) {
259
+ const current = loadState();
260
+ current.users = current.users.filter((u) => u.username !== username);
261
+ saveState(current);
262
+ return current;
263
+ }
264
+ function addGroupState(group) {
265
+ const current = loadState();
266
+ const existingIndex = current.groups.findIndex((g) => g.name === group.name);
267
+ if (existingIndex >= 0) {
268
+ current.groups[existingIndex] = group;
269
+ } else {
270
+ current.groups.push(group);
271
+ }
272
+ saveState(current);
273
+ return current;
274
+ }
275
+ function removeGroupState(name) {
276
+ const current = loadState();
277
+ current.groups = current.groups.filter((g) => g.name !== name);
278
+ saveState(current);
279
+ return current;
280
+ }
203
281
  function addConnectedIntegration(integrationId) {
204
282
  const current = loadState();
205
283
  if (!current.agenco.connectedIntegrations.includes(integrationId)) {
@@ -208,8 +286,305 @@ function addConnectedIntegration(integrationId) {
208
286
  }
209
287
  return current;
210
288
  }
289
+ function removeConnectedIntegration(integrationId) {
290
+ const current = loadState();
291
+ current.agenco.connectedIntegrations = current.agenco.connectedIntegrations.filter(
292
+ (id) => id !== integrationId
293
+ );
294
+ saveState(current);
295
+ return current;
296
+ }
297
+ function initializeState() {
298
+ const statePath = getStatePath();
299
+ if (!fs2.existsSync(statePath)) {
300
+ const state = getDefaultState();
301
+ saveState(state);
302
+ return state;
303
+ }
304
+ return loadState();
305
+ }
306
+ var init_state = __esm({
307
+ "libs/shield-daemon/src/state/index.ts"() {
308
+ "use strict";
309
+ init_paths();
310
+ }
311
+ });
312
+
313
+ // libs/shield-daemon/src/command-sync.ts
314
+ var command_sync_exports = {};
315
+ __export(command_sync_exports, {
316
+ ensureWrappersInstalled: () => ensureWrappersInstalled,
317
+ syncCommandPolicies: () => syncCommandPolicies,
318
+ syncCommandPoliciesAndWrappers: () => syncCommandPoliciesAndWrappers
319
+ });
320
+ import * as fs5 from "node:fs";
321
+ import * as path5 from "node:path";
322
+ import { execSync as execSync3 } from "node:child_process";
323
+ import { PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS } from "@agenshield/sandbox";
324
+ function resolveCommandPaths(name) {
325
+ const paths = [];
326
+ for (const dir of BIN_SEARCH_DIRS) {
327
+ const candidate = path5.join(dir, name);
328
+ try {
329
+ if (fs5.existsSync(candidate)) {
330
+ const stat = fs5.statSync(candidate);
331
+ if (stat.isFile() && (stat.mode & 73) !== 0) {
332
+ paths.push(candidate);
333
+ }
334
+ }
335
+ } catch {
336
+ }
337
+ }
338
+ if (paths.length === 0) {
339
+ try {
340
+ const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
341
+ if (result && path5.isAbsolute(result)) {
342
+ paths.push(result);
343
+ }
344
+ } catch {
345
+ }
346
+ }
347
+ return paths;
348
+ }
349
+ function extractCommandName(pattern) {
350
+ return pattern.trim().split(/\s+/)[0];
351
+ }
352
+ function extractPolicyCommandNames(policies) {
353
+ const names = /* @__PURE__ */ new Set();
354
+ for (const p of policies) {
355
+ if (p.target === "command" && p.action === "allow" && p.enabled) {
356
+ for (const pattern of p.patterns) {
357
+ const name = extractCommandName(pattern);
358
+ if (name) names.add(name);
359
+ }
360
+ }
361
+ }
362
+ return names;
363
+ }
364
+ function syncCommandPolicies(policies, logger) {
365
+ const log = logger ?? noop2;
366
+ const commandPolicies = policies.filter(
367
+ (p) => p.target === "command" && p.action === "allow" && p.enabled
368
+ );
369
+ const commandNames = /* @__PURE__ */ new Set();
370
+ for (const policy of commandPolicies) {
371
+ for (const pattern of policy.patterns) {
372
+ const name = extractCommandName(pattern);
373
+ if (name) commandNames.add(name);
374
+ }
375
+ }
376
+ const commands = [];
377
+ const now = (/* @__PURE__ */ new Date()).toISOString();
378
+ for (const name of commandNames) {
379
+ const paths = resolveCommandPaths(name);
380
+ if (paths.length === 0) {
381
+ log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
382
+ }
383
+ commands.push({
384
+ name,
385
+ paths,
386
+ addedAt: now,
387
+ addedBy: "policy",
388
+ category: "policy-managed"
389
+ });
390
+ }
391
+ const config = {
392
+ version: "1.0.0",
393
+ commands
394
+ };
395
+ const json = JSON.stringify(config, null, 2) + "\n";
396
+ try {
397
+ const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
398
+ if (!fs5.existsSync(dir)) {
399
+ fs5.mkdirSync(dir, { recursive: true });
400
+ }
401
+ fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
402
+ log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
403
+ } catch {
404
+ log.warn(`[command-sync] cannot write to ${ALLOWED_COMMANDS_PATH} (broker forwards to daemon for policy checks)`);
405
+ }
406
+ }
407
+ function generateFallbackWrapper(cmd) {
408
+ return [
409
+ "#!/bin/bash",
410
+ `# ${cmd} - AgenShield proxy (auto-generated)`,
411
+ "if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi",
412
+ `exec /opt/agenshield/bin/shield-client exec ${cmd} "$@"`,
413
+ ""
414
+ ].join("\n");
415
+ }
416
+ function installWrappersInDir(binDir, log, policyCommands) {
417
+ const shieldExecPath = "/opt/agenshield/bin/shield-exec";
418
+ if (!fs5.existsSync(binDir)) {
419
+ try {
420
+ fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
421
+ } catch {
422
+ log.warn(`[command-sync] cannot create bin dir ${binDir}`);
423
+ return;
424
+ }
425
+ }
426
+ const hasShieldExec = fs5.existsSync(shieldExecPath);
427
+ if (!hasShieldExec) {
428
+ log.warn("[command-sync] shield-exec not found, using bash wrapper fallback");
429
+ }
430
+ for (const cmd of ALL_PROXIED_COMMANDS) {
431
+ const wrapperPath = path5.join(binDir, cmd);
432
+ if (fs5.existsSync(wrapperPath)) {
433
+ continue;
434
+ }
435
+ if (hasShieldExec) {
436
+ try {
437
+ fs5.symlinkSync(shieldExecPath, wrapperPath);
438
+ continue;
439
+ } catch {
440
+ log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
441
+ }
442
+ }
443
+ try {
444
+ fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
445
+ } catch {
446
+ log.warn(`[command-sync] cannot write wrapper for ${cmd}`);
447
+ }
448
+ }
449
+ if (policyCommands) {
450
+ for (const cmd of policyCommands) {
451
+ if (PROXIED_COMMANDS_SET.has(cmd)) continue;
452
+ if (BASIC_SYSTEM_COMMANDS_SET.has(cmd)) continue;
453
+ if (SPECIALIZED_WRAPPER_COMMANDS.has(cmd)) continue;
454
+ const wrapperPath = path5.join(binDir, cmd);
455
+ if (fs5.existsSync(wrapperPath)) continue;
456
+ if (hasShieldExec) {
457
+ try {
458
+ fs5.symlinkSync(shieldExecPath, wrapperPath);
459
+ log.info(`[command-sync] installed dynamic wrapper (symlink): ${cmd}`);
460
+ continue;
461
+ } catch {
462
+ log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
463
+ }
464
+ }
465
+ try {
466
+ fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
467
+ log.info(`[command-sync] installed dynamic wrapper (bash): ${cmd}`);
468
+ } catch {
469
+ log.warn(`[command-sync] cannot write dynamic wrapper for ${cmd}`);
470
+ }
471
+ }
472
+ }
473
+ }
474
+ function cleanupStaleWrappers(binDir, policyCommands, log) {
475
+ const shieldExecPath = "/opt/agenshield/bin/shield-exec";
476
+ let entries;
477
+ try {
478
+ entries = fs5.readdirSync(binDir);
479
+ } catch {
480
+ return;
481
+ }
482
+ for (const entry of entries) {
483
+ if (PROXIED_COMMANDS_SET.has(entry)) continue;
484
+ if (SPECIALIZED_WRAPPER_COMMANDS.has(entry)) continue;
485
+ if (BASIC_SYSTEM_COMMANDS_SET.has(entry)) continue;
486
+ if (policyCommands.has(entry)) continue;
487
+ const wrapperPath = path5.join(binDir, entry);
488
+ try {
489
+ const stat = fs5.lstatSync(wrapperPath);
490
+ if (stat.isSymbolicLink()) {
491
+ const target = fs5.readlinkSync(wrapperPath);
492
+ if (target === shieldExecPath) {
493
+ fs5.unlinkSync(wrapperPath);
494
+ log.info(`[command-sync] removed stale dynamic wrapper (symlink): ${entry}`);
495
+ }
496
+ } else if (stat.isFile()) {
497
+ const content = fs5.readFileSync(wrapperPath, "utf-8");
498
+ if (content.includes("shield-client exec") && content.includes("AgenShield proxy (auto-generated)")) {
499
+ fs5.unlinkSync(wrapperPath);
500
+ log.info(`[command-sync] removed stale dynamic wrapper (bash): ${entry}`);
501
+ }
502
+ }
503
+ } catch {
504
+ }
505
+ }
506
+ }
507
+ function ensureWrappersInstalled(state, logger, policyCommands) {
508
+ const log = logger ?? noop2;
509
+ const agentUser = state.users.find((u) => u.type === "agent");
510
+ if (!agentUser) {
511
+ log.warn("[command-sync] no agent user in state, skipping wrapper installation");
512
+ return;
513
+ }
514
+ const agentBinDir = path5.join(agentUser.homeDir, "bin");
515
+ log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
516
+ installWrappersInDir(agentBinDir, log, policyCommands);
517
+ if (policyCommands) {
518
+ cleanupStaleWrappers(agentBinDir, policyCommands, log);
519
+ }
520
+ const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
521
+ if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
522
+ const envBinDir = path5.join(agentHomeEnv, "bin");
523
+ log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
524
+ installWrappersInDir(envBinDir, log, policyCommands);
525
+ if (policyCommands) {
526
+ cleanupStaleWrappers(envBinDir, policyCommands, log);
527
+ }
528
+ }
529
+ }
530
+ function syncCommandPoliciesAndWrappers(policies, state, logger) {
531
+ const policyCommands = extractPolicyCommandNames(policies);
532
+ syncCommandPolicies(policies, logger);
533
+ ensureWrappersInstalled(state, logger, policyCommands);
534
+ }
535
+ var noop2, ALLOWED_COMMANDS_PATH, BIN_SEARCH_DIRS, ALL_PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS_SET, SPECIALIZED_WRAPPER_COMMANDS, PROXIED_COMMANDS_SET;
536
+ var init_command_sync = __esm({
537
+ "libs/shield-daemon/src/command-sync.ts"() {
538
+ "use strict";
539
+ noop2 = { warn() {
540
+ }, info() {
541
+ } };
542
+ ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
543
+ BIN_SEARCH_DIRS = [
544
+ "/usr/bin",
545
+ "/usr/local/bin",
546
+ "/opt/homebrew/bin",
547
+ "/usr/sbin",
548
+ "/usr/local/sbin",
549
+ "/opt/homebrew/sbin"
550
+ ];
551
+ ALL_PROXIED_COMMANDS = [...PROXIED_COMMANDS];
552
+ BASIC_SYSTEM_COMMANDS_SET = new Set(BASIC_SYSTEM_COMMANDS);
553
+ SPECIALIZED_WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["node", "python", "python3"]);
554
+ PROXIED_COMMANDS_SET = new Set(ALL_PROXIED_COMMANDS);
555
+ }
556
+ });
557
+
558
+ // libs/shield-daemon/src/server.ts
559
+ import * as fs19 from "node:fs";
560
+ import Fastify from "fastify";
561
+ import cors from "@fastify/cors";
562
+ import fastifyStatic from "@fastify/static";
563
+
564
+ // libs/shield-daemon/src/routes/index.ts
565
+ import { API_PREFIX } from "@agenshield/ipc";
566
+
567
+ // libs/shield-daemon/src/routes/health.ts
568
+ async function healthRoutes(app) {
569
+ app.get("/health", async () => {
570
+ return {
571
+ success: true,
572
+ data: {
573
+ ok: true,
574
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
575
+ mode: "daemon"
576
+ }
577
+ };
578
+ });
579
+ }
580
+
581
+ // libs/shield-daemon/src/config/index.ts
582
+ init_paths();
583
+ init_defaults();
584
+ init_loader();
211
585
 
212
586
  // libs/shield-daemon/src/routes/status.ts
587
+ init_state();
213
588
  var startedAt = /* @__PURE__ */ new Date();
214
589
  async function statusRoutes(app) {
215
590
  app.get("/status", async () => {
@@ -236,8 +611,9 @@ async function statusRoutes(app) {
236
611
  }
237
612
 
238
613
  // libs/shield-daemon/src/routes/config.ts
239
- import * as fs7 from "node:fs";
240
- import * as path7 from "node:path";
614
+ import * as fs6 from "node:fs";
615
+ import * as path6 from "node:path";
616
+ init_state();
241
617
 
242
618
  // libs/shield-daemon/src/vault/index.ts
243
619
  import * as fs3 from "node:fs";
@@ -298,6 +674,7 @@ function decrypt(encryptedData, key) {
298
674
  }
299
675
 
300
676
  // libs/shield-daemon/src/vault/index.ts
677
+ init_paths();
301
678
  var Vault = class {
302
679
  key;
303
680
  vaultPath;
@@ -598,21 +975,21 @@ function operationsToAclPerms(operations) {
598
975
  }
599
976
  return perms.join(",");
600
977
  }
601
- function addGroupAcl(targetPath, groupName, permissions, log = noop) {
978
+ function addUserAcl(targetPath, userName, permissions, log = noop) {
602
979
  try {
603
980
  if (!fs4.existsSync(targetPath)) {
604
981
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
605
982
  return;
606
983
  }
607
984
  execSync2(
608
- `sudo chmod +a "group:${groupName} allow ${permissions}" "${targetPath}"`,
985
+ `sudo chmod +a "user:${userName} allow ${permissions}" "${targetPath}"`,
609
986
  { stdio: "pipe" }
610
987
  );
611
988
  } catch (err) {
612
989
  log.warn(`[acl] failed to add ACL on ${targetPath}: ${err.message}`);
613
990
  }
614
991
  }
615
- function removeGroupAcl(targetPath, groupName, log = noop) {
992
+ function removeUserAcl(targetPath, userName, log = noop) {
616
993
  try {
617
994
  if (!fs4.existsSync(targetPath)) {
618
995
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
@@ -624,8 +1001,8 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
624
1001
  });
625
1002
  const indices = [];
626
1003
  for (const line of output.split("\n")) {
627
- const match = line.match(/^\s*(\d+):\s+group:(\S+)\s+allow/);
628
- if (match && match[2] === groupName) {
1004
+ const match = line.match(/^\s*(\d+):\s+user:(\S+)\s+/);
1005
+ if (match && match[2] === userName) {
629
1006
  indices.push(Number(match[1]));
630
1007
  }
631
1008
  }
@@ -665,7 +1042,7 @@ function mergePerms(a, b) {
665
1042
  }
666
1043
  function computeAclMap(policies) {
667
1044
  const aclMap = /* @__PURE__ */ new Map();
668
- for (const policy of policies) {
1045
+ for (const policy of policies.filter((p) => p.action === "allow")) {
669
1046
  const perms = operationsToAclPerms(policy.operations ?? []);
670
1047
  if (!perms) continue;
671
1048
  for (const pattern of policy.patterns) {
@@ -674,7 +1051,7 @@ function computeAclMap(policies) {
674
1051
  aclMap.set(target, existing ? mergePerms(existing, perms) : perms);
675
1052
  }
676
1053
  }
677
- for (const policy of policies) {
1054
+ for (const policy of policies.filter((p) => p.action === "allow")) {
678
1055
  const perms = operationsToAclPerms(policy.operations ?? []);
679
1056
  if (!perms) continue;
680
1057
  for (const pattern of policy.patterns) {
@@ -688,282 +1065,25 @@ function computeAclMap(policies) {
688
1065
  }
689
1066
  return aclMap;
690
1067
  }
691
- function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
1068
+ function syncFilesystemPolicyAcls(oldPolicies, newPolicies, userName, logger) {
692
1069
  const log = logger ?? noop;
693
1070
  const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
694
1071
  const newFs = newPolicies.filter((p) => p.target === "filesystem");
695
1072
  const oldAclMap = computeAclMap(oldFs);
696
1073
  const newAclMap = computeAclMap(newFs);
697
- for (const oldPath of oldAclMap.keys()) {
698
- if (!newAclMap.has(oldPath)) {
699
- removeGroupAcl(oldPath, groupName, log);
700
- }
701
- }
702
- for (const [targetPath, perms] of newAclMap) {
703
- addGroupAcl(targetPath, groupName, perms, log);
704
- }
705
- }
706
-
707
- // libs/shield-daemon/src/command-sync.ts
708
- import * as fs5 from "node:fs";
709
- import * as path5 from "node:path";
710
- import { execSync as execSync3 } from "node:child_process";
711
- var noop2 = { warn() {
712
- }, info() {
713
- } };
714
- var ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
715
- var BIN_SEARCH_DIRS = [
716
- "/usr/bin",
717
- "/usr/local/bin",
718
- "/opt/homebrew/bin",
719
- "/usr/sbin",
720
- "/usr/local/sbin",
721
- "/opt/homebrew/sbin"
722
- ];
723
- var ALL_PROXIED_COMMANDS = [
724
- "curl",
725
- "wget",
726
- "git",
727
- "ssh",
728
- "scp",
729
- "rsync",
730
- "brew",
731
- "npm",
732
- "npx",
733
- "pip",
734
- "pip3",
735
- "open-url",
736
- "shieldctl",
737
- "agenco"
738
- ];
739
- function resolveCommandPaths(name) {
740
- const paths = [];
741
- for (const dir of BIN_SEARCH_DIRS) {
742
- const candidate = path5.join(dir, name);
743
- try {
744
- if (fs5.existsSync(candidate)) {
745
- const stat = fs5.statSync(candidate);
746
- if (stat.isFile() && (stat.mode & 73) !== 0) {
747
- paths.push(candidate);
748
- }
749
- }
750
- } catch {
751
- }
752
- }
753
- if (paths.length === 0) {
754
- try {
755
- const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
756
- if (result && path5.isAbsolute(result)) {
757
- paths.push(result);
758
- }
759
- } catch {
760
- }
761
- }
762
- return paths;
763
- }
764
- function extractCommandName(pattern) {
765
- return pattern.trim().split(/\s+/)[0];
766
- }
767
- function syncCommandPolicies(policies, logger) {
768
- const log = logger ?? noop2;
769
- const commandPolicies = policies.filter(
770
- (p) => p.target === "command" && p.action === "allow" && p.enabled
771
- );
772
- const commandNames = /* @__PURE__ */ new Set();
773
- for (const policy of commandPolicies) {
774
- for (const pattern of policy.patterns) {
775
- const name = extractCommandName(pattern);
776
- if (name) commandNames.add(name);
777
- }
778
- }
779
- const commands = [];
780
- const now = (/* @__PURE__ */ new Date()).toISOString();
781
- for (const name of commandNames) {
782
- const paths = resolveCommandPaths(name);
783
- if (paths.length === 0) {
784
- log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
785
- }
786
- commands.push({
787
- name,
788
- paths,
789
- addedAt: now,
790
- addedBy: "policy",
791
- category: "policy-managed"
792
- });
793
- }
794
- const config = {
795
- version: "1.0.0",
796
- commands
797
- };
798
- const json = JSON.stringify(config, null, 2) + "\n";
799
- try {
800
- const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
801
- if (!fs5.existsSync(dir)) {
802
- fs5.mkdirSync(dir, { recursive: true });
803
- }
804
- fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
805
- log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
806
- } catch {
807
- try {
808
- execSync3(`sudo -n tee "${ALLOWED_COMMANDS_PATH}" > /dev/null`, {
809
- input: json,
810
- stdio: ["pipe", "pipe", "pipe"],
811
- timeout: 5e3
812
- });
813
- log.info(`[command-sync] wrote ${commands.length} commands to allowlist (via sudo)`);
814
- } catch (sudoErr) {
815
- log.warn(`[command-sync] cannot write to ${ALLOWED_COMMANDS_PATH} \u2014 fix with: sudo chmod 775 ${path5.dirname(ALLOWED_COMMANDS_PATH)}`);
816
- log.warn(`[command-sync] error: ${sudoErr.message}`);
817
- }
818
- }
819
- }
820
- function installWrappersInDir(binDir, log) {
821
- const shieldExecPath = "/opt/agenshield/bin/shield-exec";
822
- if (!fs5.existsSync(binDir)) {
823
- try {
824
- fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
825
- } catch {
826
- try {
827
- execSync3(`sudo -n mkdir -p "${binDir}" && sudo -n chmod 755 "${binDir}"`, {
828
- stdio: "pipe",
829
- timeout: 5e3
830
- });
831
- } catch (sudoErr) {
832
- log.warn(`[command-sync] failed to create bin dir ${binDir}: ${sudoErr.message}`);
833
- return;
834
- }
835
- }
836
- }
837
- if (!fs5.existsSync(shieldExecPath)) {
838
- log.warn(`[command-sync] shield-exec not found at ${shieldExecPath}, skipping wrapper installation`);
839
- return;
840
- }
841
- for (const cmd of ALL_PROXIED_COMMANDS) {
842
- const wrapperPath = path5.join(binDir, cmd);
843
- if (fs5.existsSync(wrapperPath)) {
844
- continue;
845
- }
846
- try {
847
- fs5.symlinkSync(shieldExecPath, wrapperPath);
848
- } catch {
849
- try {
850
- execSync3(`sudo -n ln -s "${shieldExecPath}" "${wrapperPath}"`, {
851
- stdio: "pipe",
852
- timeout: 5e3
853
- });
854
- } catch (sudoErr) {
855
- log.warn(`[command-sync] failed to create wrapper ${wrapperPath}: ${sudoErr.message}`);
856
- }
857
- }
858
- }
859
- }
860
- function ensureWrappersInstalled(state, logger) {
861
- const log = logger ?? noop2;
862
- const agentUser = state.users.find((u) => u.type === "agent");
863
- if (!agentUser) {
864
- log.warn("[command-sync] no agent user in state, skipping wrapper installation");
865
- return;
866
- }
867
- const agentBinDir = path5.join(agentUser.homeDir, "bin");
868
- log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
869
- installWrappersInDir(agentBinDir, log);
870
- const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
871
- if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
872
- const envBinDir = path5.join(agentHomeEnv, "bin");
873
- log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
874
- installWrappersInDir(envBinDir, log);
875
- }
876
- }
877
- function syncCommandPoliciesAndWrappers(policies, state, logger) {
878
- syncCommandPolicies(policies, logger);
879
- ensureWrappersInstalled(state, logger);
880
- }
881
-
882
- // libs/shield-daemon/src/policy-sync.ts
883
- import * as fs6 from "node:fs";
884
- import * as path6 from "node:path";
885
- import { execSync as execSync4 } from "node:child_process";
886
- var noop3 = { warn() {
887
- }, info() {
888
- } };
889
- var BROKER_POLICIES_DIR = "/opt/agenshield/policies";
890
- var USER_POLICIES_FILE = path6.join(BROKER_POLICIES_DIR, "custom", "user-policies.json");
891
- var BROKER_TARGETS = /* @__PURE__ */ new Set(["url", "command", "skill"]);
892
- function normalizeUrlPatterns(pattern) {
893
- let p = pattern.trim();
894
- p = p.replace(/\/+$/, "");
895
- if (!p.match(/^(\*|https?):\/\//i)) {
896
- p = `https://${p}`;
897
- }
898
- if (p.endsWith("*")) {
899
- const base = p.replace(/\/\*+$/, "");
900
- if (base !== p) {
901
- return [base, p];
902
- }
903
- return [p];
904
- }
905
- return [p, `${p}/**`];
906
- }
907
- function inferOperations(target) {
908
- switch (target) {
909
- case "url":
910
- return ["http_request", "open_url"];
911
- case "command":
912
- return ["exec"];
913
- case "skill":
914
- return ["skill_install", "skill_uninstall"];
915
- default:
916
- return ["*"];
917
- }
918
- }
919
- function toBrokerRule(policy) {
920
- let patterns;
921
- if (policy.target === "url") {
922
- patterns = policy.patterns.flatMap(normalizeUrlPatterns);
923
- patterns = [...new Set(patterns)];
924
- } else {
925
- patterns = [...policy.patterns];
926
- }
927
- return {
928
- id: policy.id,
929
- name: policy.name,
930
- action: policy.action,
931
- target: policy.target,
932
- operations: policy.operations?.length ? [...policy.operations] : inferOperations(policy.target),
933
- patterns,
934
- enabled: policy.enabled,
935
- priority: policy.priority ?? 0
936
- };
937
- }
938
- function syncPoliciesToBrokerDir(policies, logger) {
939
- const log = logger ?? noop3;
940
- const applicable = policies.filter((p) => BROKER_TARGETS.has(p.target));
941
- const rules = applicable.map(toBrokerRule);
942
- const payload = { rules };
943
- const json = JSON.stringify(payload, null, 2) + "\n";
944
- try {
945
- const dir = path6.dirname(USER_POLICIES_FILE);
946
- if (!fs6.existsSync(dir)) {
947
- fs6.mkdirSync(dir, { recursive: true });
948
- }
949
- fs6.writeFileSync(USER_POLICIES_FILE, json, "utf-8");
950
- log.info(`[policy-sync] wrote ${rules.length} user policies to broker dir`);
951
- } catch {
952
- try {
953
- execSync4(`sudo -n tee "${USER_POLICIES_FILE}" > /dev/null`, {
954
- input: json,
955
- stdio: ["pipe", "pipe", "pipe"],
956
- timeout: 5e3
957
- });
958
- log.info(`[policy-sync] wrote ${rules.length} user policies to broker dir (via sudo)`);
959
- } catch (sudoErr) {
960
- log.warn(`[policy-sync] cannot write to ${USER_POLICIES_FILE} \u2014 fix with: sudo chmod 775 ${path6.dirname(USER_POLICIES_FILE)}`);
961
- log.warn(`[policy-sync] error: ${sudoErr.message}`);
1074
+ const allPaths = /* @__PURE__ */ new Set([...oldAclMap.keys(), ...newAclMap.keys()]);
1075
+ for (const targetPath of allPaths) {
1076
+ removeUserAcl(targetPath, userName, log);
1077
+ const newPerms = newAclMap.get(targetPath);
1078
+ if (newPerms) {
1079
+ addUserAcl(targetPath, userName, newPerms, log);
962
1080
  }
963
1081
  }
964
1082
  }
965
1083
 
966
1084
  // libs/shield-daemon/src/routes/config.ts
1085
+ init_command_sync();
1086
+ import { installShieldExec, createUserConfig } from "@agenshield/sandbox";
967
1087
  async function configRoutes(app) {
968
1088
  app.get("/config", async () => {
969
1089
  const config = loadConfig();
@@ -980,12 +1100,11 @@ async function configRoutes(app) {
980
1100
  const updated = updateConfig(request.body);
981
1101
  if (request.body.policies) {
982
1102
  const state = loadState();
983
- const wsGroup = state.groups.find((g) => g.type === "workspace");
984
- if (wsGroup) {
985
- syncFilesystemPolicyAcls(oldPolicies, updated.policies, wsGroup.name, app.log);
1103
+ const agentUser = state.users.find((u) => u.type === "agent");
1104
+ if (agentUser) {
1105
+ syncFilesystemPolicyAcls(oldPolicies, updated.policies, agentUser.username, app.log);
986
1106
  }
987
1107
  syncCommandPoliciesAndWrappers(updated.policies, state, app.log);
988
- syncPoliciesToBrokerDir(updated.policies, app.log);
989
1108
  }
990
1109
  return {
991
1110
  success: true,
@@ -1006,12 +1125,11 @@ async function configRoutes(app) {
1006
1125
  try {
1007
1126
  const oldConfig = loadConfig();
1008
1127
  const state = loadState();
1009
- const wsGroup = state.groups.find((g) => g.type === "workspace");
1010
- if (wsGroup) {
1011
- syncFilesystemPolicyAcls(oldConfig.policies, [], wsGroup.name, app.log);
1128
+ const agentUser = state.users.find((u) => u.type === "agent");
1129
+ if (agentUser) {
1130
+ syncFilesystemPolicyAcls(oldConfig.policies, [], agentUser.username, app.log);
1012
1131
  }
1013
1132
  syncCommandPoliciesAndWrappers([], state, app.log);
1014
- syncPoliciesToBrokerDir([], app.log);
1015
1133
  saveConfig(getDefaultConfig());
1016
1134
  const vault = getVault();
1017
1135
  await vault.destroy();
@@ -1027,9 +1145,31 @@ async function configRoutes(app) {
1027
1145
  };
1028
1146
  }
1029
1147
  });
1148
+ app.post("/config/install-wrappers", async () => {
1149
+ try {
1150
+ const state = loadState();
1151
+ const agentUser = state.users.find((u) => u.type === "agent");
1152
+ if (!agentUser) {
1153
+ return { success: false, error: "No agent user found in state" };
1154
+ }
1155
+ const userConfig = createUserConfig();
1156
+ const binDir = path6.join(agentUser.homeDir, "bin");
1157
+ const result = await installShieldExec(userConfig, binDir);
1158
+ return {
1159
+ success: result.success,
1160
+ installed: result.installed,
1161
+ error: result.error
1162
+ };
1163
+ } catch (error) {
1164
+ return {
1165
+ success: false,
1166
+ error: error instanceof Error ? error.message : "Failed to install wrappers"
1167
+ };
1168
+ }
1169
+ });
1030
1170
  app.get("/config/openclaw", async (_request, reply) => {
1031
1171
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
1032
- const configDir = path7.join(agentHome, ".openclaw");
1172
+ const configDir = path6.join(agentHome, ".openclaw");
1033
1173
  const configFiles = readConfigDir(configDir);
1034
1174
  return reply.send({ configDir, files: configFiles });
1035
1175
  });
@@ -1041,7 +1181,7 @@ async function configRoutes(app) {
1041
1181
  return reply.code(400).send({ error: "original query param required" });
1042
1182
  }
1043
1183
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
1044
- const agentConfigDir = path7.join(agentHome, ".openclaw");
1184
+ const agentConfigDir = path6.join(agentHome, ".openclaw");
1045
1185
  const diff = diffConfigDirs(original, agentConfigDir);
1046
1186
  return reply.send({ diff });
1047
1187
  }
@@ -1051,27 +1191,27 @@ var SKIP_DIRS = /* @__PURE__ */ new Set(["skills", "node_modules", ".git", "dist
1051
1191
  function readConfigDir(dir, base) {
1052
1192
  const result = {};
1053
1193
  const root = base ?? dir;
1054
- if (!fs7.existsSync(dir)) {
1194
+ if (!fs6.existsSync(dir)) {
1055
1195
  return result;
1056
1196
  }
1057
1197
  let entries;
1058
1198
  try {
1059
- entries = fs7.readdirSync(dir, { withFileTypes: true });
1199
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
1060
1200
  } catch {
1061
1201
  return result;
1062
1202
  }
1063
1203
  for (const entry of entries) {
1064
1204
  if (entry.isDirectory()) {
1065
1205
  if (SKIP_DIRS.has(entry.name)) continue;
1066
- const sub = readConfigDir(path7.join(dir, entry.name), root);
1206
+ const sub = readConfigDir(path6.join(dir, entry.name), root);
1067
1207
  Object.assign(result, sub);
1068
1208
  } else if (entry.isFile()) {
1069
- const ext = path7.extname(entry.name).toLowerCase();
1209
+ const ext = path6.extname(entry.name).toLowerCase();
1070
1210
  if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
1071
- const filePath = path7.join(dir, entry.name);
1072
- const relPath = path7.relative(root, filePath);
1211
+ const filePath = path6.join(dir, entry.name);
1212
+ const relPath = path6.relative(root, filePath);
1073
1213
  try {
1074
- result[relPath] = fs7.readFileSync(filePath, "utf-8");
1214
+ result[relPath] = fs6.readFileSync(filePath, "utf-8");
1075
1215
  } catch {
1076
1216
  }
1077
1217
  }
@@ -1191,10 +1331,10 @@ function emitSecurityWarning(warning) {
1191
1331
  function emitSecurityCritical(issue) {
1192
1332
  daemonEvents.broadcast("security:critical", { message: issue });
1193
1333
  }
1194
- function emitApiRequest(method, path20, statusCode, duration, requestBody, responseBody) {
1334
+ function emitApiRequest(method, path19, statusCode, duration, requestBody, responseBody) {
1195
1335
  daemonEvents.broadcast("api:request", {
1196
1336
  method,
1197
- path: path20,
1337
+ path: path19,
1198
1338
  statusCode,
1199
1339
  duration,
1200
1340
  ...requestBody !== void 0 && { requestBody },
@@ -1237,6 +1377,7 @@ function emitEvent(type, data) {
1237
1377
 
1238
1378
  // libs/shield-daemon/src/auth/passcode.ts
1239
1379
  import * as crypto3 from "node:crypto";
1380
+ init_state();
1240
1381
  import { DEFAULT_AUTH_CONFIG as DEFAULT_AUTH_CONFIG2 } from "@agenshield/ipc";
1241
1382
  var ITERATIONS = 1e5;
1242
1383
  var KEY_LENGTH = 64;
@@ -1401,16 +1542,16 @@ var PROTECTED_ROUTES = [
1401
1542
  { method: "POST", path: "/api/config/factory-reset" },
1402
1543
  { method: "POST", path: "/api/skills/install" }
1403
1544
  ];
1404
- function isProtectedRoute(method, path20) {
1545
+ function isProtectedRoute(method, path19) {
1405
1546
  return PROTECTED_ROUTES.some(
1406
- (route) => route.method === method && path20.startsWith(route.path)
1547
+ (route) => route.method === method && path19.startsWith(route.path)
1407
1548
  );
1408
1549
  }
1409
1550
  function createAuthHook() {
1410
1551
  return async (request, reply) => {
1411
1552
  const method = request.method;
1412
- const path20 = request.url.split("?")[0];
1413
- if (!isProtectedRoute(method, path20)) {
1553
+ const path19 = request.url.split("?")[0];
1554
+ if (!isProtectedRoute(method, path19)) {
1414
1555
  return;
1415
1556
  }
1416
1557
  if (!isAuthenticated(request)) {
@@ -1917,6 +2058,9 @@ async function loggedFetch(url, init, context) {
1917
2058
  }
1918
2059
  }
1919
2060
 
2061
+ // libs/shield-daemon/src/routes/agenco.ts
2062
+ init_state();
2063
+
1920
2064
  // libs/shield-daemon/src/mcp/client.ts
1921
2065
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1922
2066
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -2242,8 +2386,8 @@ function getMCPState() {
2242
2386
  }
2243
2387
 
2244
2388
  // libs/shield-daemon/src/services/integration-skills.ts
2245
- import * as fs9 from "node:fs";
2246
- import * as path9 from "node:path";
2389
+ import * as fs8 from "node:fs";
2390
+ import * as path8 from "node:path";
2247
2391
 
2248
2392
  // libs/shield-skills/dist/index.js
2249
2393
  import * as path22 from "node:path";
@@ -2308,9 +2452,9 @@ var __skills_dirname = path22.dirname(fileURLToPath(import.meta.url));
2308
2452
  var BUILTIN_SKILLS_DIR = path22.resolve(__skills_dirname, "..", "skills");
2309
2453
 
2310
2454
  // libs/shield-daemon/src/watchers/skills.ts
2311
- import * as fs8 from "node:fs";
2312
- import * as path8 from "node:path";
2313
- import { execSync as execSync5 } from "node:child_process";
2455
+ import * as fs7 from "node:fs";
2456
+ import * as path7 from "node:path";
2457
+ import { execSync as execSync4 } from "node:child_process";
2314
2458
  var APPROVED_SKILLS_PATH = "/opt/agenshield/config/approved-skills.json";
2315
2459
  var QUARANTINE_DIR = "/opt/agenshield/quarantine/skills";
2316
2460
  var DEBOUNCE_MS = 500;
@@ -2321,8 +2465,8 @@ var skillsDir = "";
2321
2465
  var callbacks = {};
2322
2466
  function loadApprovedSkills() {
2323
2467
  try {
2324
- if (fs8.existsSync(APPROVED_SKILLS_PATH)) {
2325
- const content = fs8.readFileSync(APPROVED_SKILLS_PATH, "utf-8");
2468
+ if (fs7.existsSync(APPROVED_SKILLS_PATH)) {
2469
+ const content = fs7.readFileSync(APPROVED_SKILLS_PATH, "utf-8");
2326
2470
  return JSON.parse(content);
2327
2471
  }
2328
2472
  } catch {
@@ -2331,10 +2475,10 @@ function loadApprovedSkills() {
2331
2475
  }
2332
2476
  function saveApprovedSkills(skills) {
2333
2477
  try {
2334
- const dir = path8.dirname(APPROVED_SKILLS_PATH);
2335
- fs8.mkdirSync(dir, { recursive: true });
2478
+ const dir = path7.dirname(APPROVED_SKILLS_PATH);
2479
+ fs7.mkdirSync(dir, { recursive: true });
2336
2480
  const content = JSON.stringify(skills, null, 2);
2337
- fs8.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
2481
+ fs7.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
2338
2482
  } catch (err) {
2339
2483
  console.error("Failed to save approved skills:", err.message);
2340
2484
  }
@@ -2345,16 +2489,16 @@ function isApproved(skillName) {
2345
2489
  }
2346
2490
  function quarantineSkill(skillName, skillPath) {
2347
2491
  try {
2348
- const quarantinePath = path8.join(QUARANTINE_DIR, skillName);
2349
- if (!fs8.existsSync(QUARANTINE_DIR)) {
2350
- fs8.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
2492
+ const quarantinePath = path7.join(QUARANTINE_DIR, skillName);
2493
+ if (!fs7.existsSync(QUARANTINE_DIR)) {
2494
+ fs7.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
2351
2495
  }
2352
- if (fs8.existsSync(quarantinePath)) {
2353
- fs8.rmSync(quarantinePath, { recursive: true, force: true });
2496
+ if (fs7.existsSync(quarantinePath)) {
2497
+ fs7.rmSync(quarantinePath, { recursive: true, force: true });
2354
2498
  }
2355
- execSync5(`mv "${skillPath}" "${quarantinePath}"`, { stdio: "pipe" });
2356
- execSync5(`chown -R root:wheel "${quarantinePath}"`, { stdio: "pipe" });
2357
- execSync5(`chmod -R 700 "${quarantinePath}"`, { stdio: "pipe" });
2499
+ execSync4(`mv "${skillPath}" "${quarantinePath}"`, { stdio: "pipe" });
2500
+ execSync4(`chown -R root:wheel "${quarantinePath}"`, { stdio: "pipe" });
2501
+ execSync4(`chmod -R 700 "${quarantinePath}"`, { stdio: "pipe" });
2358
2502
  const info = {
2359
2503
  name: skillName,
2360
2504
  quarantinedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2369,16 +2513,16 @@ function quarantineSkill(skillName, skillPath) {
2369
2513
  }
2370
2514
  }
2371
2515
  function scanSkills() {
2372
- if (!skillsDir || !fs8.existsSync(skillsDir)) {
2516
+ if (!skillsDir || !fs7.existsSync(skillsDir)) {
2373
2517
  return;
2374
2518
  }
2375
2519
  try {
2376
- const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
2520
+ const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
2377
2521
  for (const entry of entries) {
2378
2522
  if (!entry.isDirectory()) continue;
2379
2523
  const skillName = entry.name;
2380
2524
  if (!isApproved(skillName)) {
2381
- const fullPath = path8.join(skillsDir, skillName);
2525
+ const fullPath = path7.join(skillsDir, skillName);
2382
2526
  const info = quarantineSkill(skillName, fullPath);
2383
2527
  if (info && callbacks.onQuarantined) {
2384
2528
  callbacks.onQuarantined(info);
@@ -2411,8 +2555,8 @@ function startSkillsWatcher(watchDir, cbs = {}, pollIntervalMs = 3e4) {
2411
2555
  callbacks = cbs;
2412
2556
  scanSkills();
2413
2557
  try {
2414
- if (fs8.existsSync(skillsDir)) {
2415
- watcher = fs8.watch(skillsDir, { persistent: false }, handleFsEvent);
2558
+ if (fs7.existsSync(skillsDir)) {
2559
+ watcher = fs7.watch(skillsDir, { persistent: false }, handleFsEvent);
2416
2560
  watcher.on("error", (err) => {
2417
2561
  console.warn("[SkillsWatcher] fs.watch error, falling back to polling:", err.message);
2418
2562
  watcher?.close();
@@ -2444,9 +2588,9 @@ function stopSkillsWatcher() {
2444
2588
  }
2445
2589
  function approveSkill(skillName) {
2446
2590
  try {
2447
- const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
2448
- const destPath = path8.join(skillsDir, skillName);
2449
- if (!fs8.existsSync(quarantinedPath)) {
2591
+ const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
2592
+ const destPath = path7.join(skillsDir, skillName);
2593
+ if (!fs7.existsSync(quarantinedPath)) {
2450
2594
  return { success: false, error: `Skill "${skillName}" not found in quarantine` };
2451
2595
  }
2452
2596
  const approved = loadApprovedSkills();
@@ -2457,9 +2601,9 @@ function approveSkill(skillName) {
2457
2601
  });
2458
2602
  saveApprovedSkills(approved);
2459
2603
  }
2460
- execSync5(`mv "${quarantinedPath}" "${destPath}"`, { stdio: "pipe" });
2461
- execSync5(`chown -R root:wheel "${destPath}"`, { stdio: "pipe" });
2462
- execSync5(`chmod -R a+rX,go-w "${destPath}"`, { stdio: "pipe" });
2604
+ execSync4(`mv "${quarantinedPath}" "${destPath}"`, { stdio: "pipe" });
2605
+ execSync4(`chown -R root:wheel "${destPath}"`, { stdio: "pipe" });
2606
+ execSync4(`chmod -R a+rX,go-w "${destPath}"`, { stdio: "pipe" });
2463
2607
  if (callbacks.onApproved) {
2464
2608
  callbacks.onApproved(skillName);
2465
2609
  }
@@ -2471,11 +2615,11 @@ function approveSkill(skillName) {
2471
2615
  }
2472
2616
  function rejectSkill(skillName) {
2473
2617
  try {
2474
- const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
2475
- if (!fs8.existsSync(quarantinedPath)) {
2618
+ const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
2619
+ if (!fs7.existsSync(quarantinedPath)) {
2476
2620
  return { success: false, error: `Skill "${skillName}" not found in quarantine` };
2477
2621
  }
2478
- fs8.rmSync(quarantinedPath, { recursive: true, force: true });
2622
+ fs7.rmSync(quarantinedPath, { recursive: true, force: true });
2479
2623
  console.log(`[SkillsWatcher] Rejected and deleted skill: ${skillName}`);
2480
2624
  return { success: true };
2481
2625
  } catch (err) {
@@ -2487,8 +2631,8 @@ function revokeSkill(skillName) {
2487
2631
  const approved = loadApprovedSkills();
2488
2632
  const filtered = approved.filter((s) => s.name !== skillName);
2489
2633
  saveApprovedSkills(filtered);
2490
- const skillPath = path8.join(skillsDir, skillName);
2491
- if (fs8.existsSync(skillPath)) {
2634
+ const skillPath = path7.join(skillsDir, skillName);
2635
+ if (fs7.existsSync(skillPath)) {
2492
2636
  quarantineSkill(skillName, skillPath);
2493
2637
  }
2494
2638
  console.log(`[SkillsWatcher] Revoked approval for skill: ${skillName}`);
@@ -2500,17 +2644,17 @@ function revokeSkill(skillName) {
2500
2644
  function listQuarantined() {
2501
2645
  const results = [];
2502
2646
  try {
2503
- if (!fs8.existsSync(QUARANTINE_DIR)) {
2647
+ if (!fs7.existsSync(QUARANTINE_DIR)) {
2504
2648
  return results;
2505
2649
  }
2506
- const entries = fs8.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
2650
+ const entries = fs7.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
2507
2651
  for (const entry of entries) {
2508
2652
  if (entry.isDirectory()) {
2509
- const stat = fs8.statSync(path8.join(QUARANTINE_DIR, entry.name));
2653
+ const stat = fs7.statSync(path7.join(QUARANTINE_DIR, entry.name));
2510
2654
  results.push({
2511
2655
  name: entry.name,
2512
2656
  quarantinedAt: stat.mtime.toISOString(),
2513
- originalPath: path8.join(skillsDir, entry.name),
2657
+ originalPath: path7.join(skillsDir, entry.name),
2514
2658
  reason: "Skill not in approved list"
2515
2659
  });
2516
2660
  }
@@ -2550,15 +2694,15 @@ function removeFromApprovedList(skillName) {
2550
2694
  // libs/shield-daemon/src/services/integration-skills.ts
2551
2695
  var AGENCO_SKILL_NAME = "agenco-secure-integrations";
2552
2696
  function copyDirSync(src, dest) {
2553
- fs9.mkdirSync(dest, { recursive: true });
2554
- const entries = fs9.readdirSync(src, { withFileTypes: true });
2697
+ fs8.mkdirSync(dest, { recursive: true });
2698
+ const entries = fs8.readdirSync(src, { withFileTypes: true });
2555
2699
  for (const entry of entries) {
2556
- const srcPath = path9.join(src, entry.name);
2557
- const destPath = path9.join(dest, entry.name);
2700
+ const srcPath = path8.join(src, entry.name);
2701
+ const destPath = path8.join(dest, entry.name);
2558
2702
  if (entry.isDirectory()) {
2559
2703
  copyDirSync(srcPath, destPath);
2560
2704
  } else {
2561
- fs9.copyFileSync(srcPath, destPath);
2705
+ fs8.copyFileSync(srcPath, destPath);
2562
2706
  }
2563
2707
  }
2564
2708
  }
@@ -2568,9 +2712,9 @@ async function provisionAgenCoSkill() {
2568
2712
  console.warn("[IntegrationSkills] Skills directory not configured \u2014 skipping provision");
2569
2713
  return { installed: false };
2570
2714
  }
2571
- const destDir = path9.join(skillsDir2, AGENCO_SKILL_NAME);
2572
- const srcDir = path9.join(BUILTIN_SKILLS_DIR, AGENCO_SKILL_NAME);
2573
- if (fs9.existsSync(destDir)) {
2715
+ const destDir = path8.join(skillsDir2, AGENCO_SKILL_NAME);
2716
+ const srcDir = path8.join(BUILTIN_SKILLS_DIR, AGENCO_SKILL_NAME);
2717
+ if (fs8.existsSync(destDir)) {
2574
2718
  return { installed: false };
2575
2719
  }
2576
2720
  try {
@@ -2581,8 +2725,8 @@ async function provisionAgenCoSkill() {
2581
2725
  } catch (err) {
2582
2726
  console.error(`[IntegrationSkills] Failed to install "${AGENCO_SKILL_NAME}":`, err.message);
2583
2727
  try {
2584
- if (fs9.existsSync(destDir)) {
2585
- fs9.rmSync(destDir, { recursive: true, force: true });
2728
+ if (fs8.existsSync(destDir)) {
2729
+ fs8.rmSync(destDir, { recursive: true, force: true });
2586
2730
  }
2587
2731
  removeFromApprovedList(AGENCO_SKILL_NAME);
2588
2732
  } catch {
@@ -2597,12 +2741,12 @@ async function provisionIntegrationSkill(integrationSlug) {
2597
2741
  return { installed: false };
2598
2742
  }
2599
2743
  const skillName = `integration-${integrationSlug}`;
2600
- const destDir = path9.join(skillsDir2, skillName);
2601
- const srcDir = path9.join(BUILTIN_SKILLS_DIR, skillName);
2602
- if (fs9.existsSync(destDir)) {
2744
+ const destDir = path8.join(skillsDir2, skillName);
2745
+ const srcDir = path8.join(BUILTIN_SKILLS_DIR, skillName);
2746
+ if (fs8.existsSync(destDir)) {
2603
2747
  return { installed: false };
2604
2748
  }
2605
- if (!fs9.existsSync(srcDir)) {
2749
+ if (!fs8.existsSync(srcDir)) {
2606
2750
  return { installed: false };
2607
2751
  }
2608
2752
  try {
@@ -2613,8 +2757,8 @@ async function provisionIntegrationSkill(integrationSlug) {
2613
2757
  } catch (err) {
2614
2758
  console.error(`[IntegrationSkills] Failed to install "${skillName}":`, err.message);
2615
2759
  try {
2616
- if (fs9.existsSync(destDir)) {
2617
- fs9.rmSync(destDir, { recursive: true, force: true });
2760
+ if (fs8.existsSync(destDir)) {
2761
+ fs8.rmSync(destDir, { recursive: true, force: true });
2618
2762
  }
2619
2763
  removeFromApprovedList(skillName);
2620
2764
  } catch {
@@ -5137,15 +5281,15 @@ async function agencoRoutes(app) {
5137
5281
  }
5138
5282
 
5139
5283
  // libs/shield-daemon/src/routes/skills.ts
5140
- import * as fs14 from "node:fs";
5141
- import * as path14 from "node:path";
5142
- import { execSync as execSync9 } from "node:child_process";
5284
+ import * as fs13 from "node:fs";
5285
+ import * as path13 from "node:path";
5286
+ import { execSync as execSync8 } from "node:child_process";
5143
5287
  import { parseSkillMd } from "@agenshield/sandbox";
5144
5288
 
5145
5289
  // libs/shield-daemon/src/services/skill-analyzer.ts
5146
- import * as fs10 from "node:fs";
5147
- import * as path10 from "node:path";
5148
- import { execSync as execSync6 } from "node:child_process";
5290
+ import * as fs9 from "node:fs";
5291
+ import * as path9 from "node:path";
5292
+ import { execSync as execSync5 } from "node:child_process";
5149
5293
  var ANALYSIS_CACHE_PATH = "/opt/agenshield/config/skill-analyses.json";
5150
5294
  var COMMAND_PATTERNS = [
5151
5295
  // Bash-style: command at start of line or after pipe/semicolon
@@ -5206,8 +5350,8 @@ var IGNORE_WORDS = /* @__PURE__ */ new Set([
5206
5350
  ]);
5207
5351
  function loadCache() {
5208
5352
  try {
5209
- if (fs10.existsSync(ANALYSIS_CACHE_PATH)) {
5210
- return JSON.parse(fs10.readFileSync(ANALYSIS_CACHE_PATH, "utf-8"));
5353
+ if (fs9.existsSync(ANALYSIS_CACHE_PATH)) {
5354
+ return JSON.parse(fs9.readFileSync(ANALYSIS_CACHE_PATH, "utf-8"));
5211
5355
  }
5212
5356
  } catch {
5213
5357
  }
@@ -5215,18 +5359,18 @@ function loadCache() {
5215
5359
  }
5216
5360
  function saveCache(cache3) {
5217
5361
  try {
5218
- const dir = path10.dirname(ANALYSIS_CACHE_PATH);
5219
- if (!fs10.existsSync(dir)) {
5220
- fs10.mkdirSync(dir, { recursive: true });
5362
+ const dir = path9.dirname(ANALYSIS_CACHE_PATH);
5363
+ if (!fs9.existsSync(dir)) {
5364
+ fs9.mkdirSync(dir, { recursive: true });
5221
5365
  }
5222
- fs10.writeFileSync(ANALYSIS_CACHE_PATH, JSON.stringify(cache3, null, 2) + "\n", "utf-8");
5366
+ fs9.writeFileSync(ANALYSIS_CACHE_PATH, JSON.stringify(cache3, null, 2) + "\n", "utf-8");
5223
5367
  } catch (err) {
5224
5368
  console.error("[SkillAnalyzer] Failed to save cache:", err.message);
5225
5369
  }
5226
5370
  }
5227
5371
  function resolveCommand(name) {
5228
5372
  try {
5229
- const result = execSync6(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
5373
+ const result = execSync5(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
5230
5374
  return result || void 0;
5231
5375
  } catch {
5232
5376
  return void 0;
@@ -5361,33 +5505,33 @@ function clearCachedAnalysis(skillName) {
5361
5505
  }
5362
5506
 
5363
5507
  // libs/shield-daemon/src/services/skill-lifecycle.ts
5364
- import * as fs11 from "node:fs";
5365
- import * as path11 from "node:path";
5366
- import { execSync as execSync7 } from "node:child_process";
5508
+ import * as fs10 from "node:fs";
5509
+ import * as path10 from "node:path";
5510
+ import { execSync as execSync6 } from "node:child_process";
5367
5511
  function createSkillWrapper(name, binDir) {
5368
- if (!fs11.existsSync(binDir)) {
5369
- fs11.mkdirSync(binDir, { recursive: true });
5512
+ if (!fs10.existsSync(binDir)) {
5513
+ fs10.mkdirSync(binDir, { recursive: true });
5370
5514
  }
5371
- const wrapperPath = path11.join(binDir, name);
5515
+ const wrapperPath = path10.join(binDir, name);
5372
5516
  const wrapperContent = `#!/bin/bash
5373
5517
  # ${name} skill wrapper - policy-enforced execution
5374
5518
  # Ensure accessible working directory
5375
5519
  if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi
5376
5520
  exec /opt/agenshield/bin/shield-client skill run "${name}" "$@"
5377
5521
  `;
5378
- fs11.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
5522
+ fs10.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
5379
5523
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
5380
5524
  try {
5381
- execSync7(`chown root:${socketGroup} "${wrapperPath}"`, { stdio: "pipe" });
5382
- execSync7(`chmod 755 "${wrapperPath}"`, { stdio: "pipe" });
5525
+ execSync6(`chown root:${socketGroup} "${wrapperPath}"`, { stdio: "pipe" });
5526
+ execSync6(`chmod 755 "${wrapperPath}"`, { stdio: "pipe" });
5383
5527
  } catch {
5384
5528
  }
5385
5529
  }
5386
5530
  function removeSkillWrapper(name, binDir) {
5387
- const wrapperPath = path11.join(binDir, name);
5531
+ const wrapperPath = path10.join(binDir, name);
5388
5532
  try {
5389
- if (fs11.existsSync(wrapperPath)) {
5390
- fs11.unlinkSync(wrapperPath);
5533
+ if (fs10.existsSync(wrapperPath)) {
5534
+ fs10.unlinkSync(wrapperPath);
5391
5535
  }
5392
5536
  } catch {
5393
5537
  }
@@ -5420,18 +5564,18 @@ function removeSkillPolicy(name) {
5420
5564
  }
5421
5565
 
5422
5566
  // libs/shield-daemon/src/services/openclaw-config.ts
5423
- import * as fs12 from "node:fs";
5424
- import * as path12 from "node:path";
5425
- import { execSync as execSync8 } from "node:child_process";
5567
+ import * as fs11 from "node:fs";
5568
+ import * as path11 from "node:path";
5569
+ import { execSync as execSync7 } from "node:child_process";
5426
5570
  function getOpenClawConfigPath() {
5427
5571
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
5428
- return path12.join(agentHome, ".openclaw", "openclaw.json");
5572
+ return path11.join(agentHome, ".openclaw", "openclaw.json");
5429
5573
  }
5430
5574
  function readConfig() {
5431
5575
  const configPath = getOpenClawConfigPath();
5432
5576
  try {
5433
- if (fs12.existsSync(configPath)) {
5434
- return JSON.parse(fs12.readFileSync(configPath, "utf-8"));
5577
+ if (fs11.existsSync(configPath)) {
5578
+ return JSON.parse(fs11.readFileSync(configPath, "utf-8"));
5435
5579
  }
5436
5580
  } catch {
5437
5581
  console.warn("[OpenClawConfig] Failed to read openclaw.json, starting fresh");
@@ -5440,14 +5584,14 @@ function readConfig() {
5440
5584
  }
5441
5585
  function writeConfig(config) {
5442
5586
  const configPath = getOpenClawConfigPath();
5443
- fs12.mkdirSync(path12.dirname(configPath), { recursive: true });
5444
- fs12.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
5587
+ fs11.mkdirSync(path11.dirname(configPath), { recursive: true });
5588
+ fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
5445
5589
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
5446
- const brokerUser = path12.basename(agentHome) + "_broker";
5590
+ const brokerUser = path11.basename(agentHome) + "_broker";
5447
5591
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
5448
5592
  try {
5449
- execSync8(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
5450
- execSync8(`chmod 664 "${configPath}"`, { stdio: "pipe" });
5593
+ execSync7(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
5594
+ execSync7(`chmod 664 "${configPath}"`, { stdio: "pipe" });
5451
5595
  } catch {
5452
5596
  }
5453
5597
  }
@@ -5476,8 +5620,8 @@ function removeSkillEntry(slug) {
5476
5620
  }
5477
5621
 
5478
5622
  // libs/shield-daemon/src/services/marketplace.ts
5479
- import * as fs13 from "node:fs";
5480
- import * as path13 from "node:path";
5623
+ import * as fs12 from "node:fs";
5624
+ import * as path12 from "node:path";
5481
5625
  import * as os4 from "node:os";
5482
5626
  import { CONFIG_DIR as CONFIG_DIR2, MARKETPLACE_DIR } from "@agenshield/ipc";
5483
5627
  var cache = /* @__PURE__ */ new Map();
@@ -5501,35 +5645,35 @@ var ANALYSIS_TIMEOUT = 4 * 6e4;
5501
5645
  var SEARCH_CACHE_TTL = 6e4;
5502
5646
  var DETAIL_CACHE_TTL = 5 * 6e4;
5503
5647
  var SHORT_TIMEOUT = 1e4;
5504
- async function convexAction(path20, args, timeout) {
5648
+ async function convexAction(path19, args, timeout) {
5505
5649
  const res = await fetch(`${CONVEX_BASE}/api/action`, {
5506
5650
  method: "POST",
5507
5651
  signal: AbortSignal.timeout(timeout),
5508
5652
  headers: { "Content-Type": "application/json" },
5509
- body: JSON.stringify({ path: path20, args, format: "json" })
5653
+ body: JSON.stringify({ path: path19, args, format: "json" })
5510
5654
  });
5511
5655
  if (!res.ok) {
5512
- throw new Error(`Convex action ${path20} returned ${res.status}`);
5656
+ throw new Error(`Convex action ${path19} returned ${res.status}`);
5513
5657
  }
5514
5658
  const body = await res.json();
5515
5659
  if (body.status === "error") {
5516
- throw new Error(`Convex action ${path20}: ${body.errorMessage ?? "unknown error"}`);
5660
+ throw new Error(`Convex action ${path19}: ${body.errorMessage ?? "unknown error"}`);
5517
5661
  }
5518
5662
  return body.value;
5519
5663
  }
5520
- async function convexQuery(path20, args, timeout) {
5664
+ async function convexQuery(path19, args, timeout) {
5521
5665
  const res = await fetch(`${CONVEX_BASE}/api/query`, {
5522
5666
  method: "POST",
5523
5667
  signal: AbortSignal.timeout(timeout),
5524
5668
  headers: { "Content-Type": "application/json" },
5525
- body: JSON.stringify({ path: path20, args, format: "json" })
5669
+ body: JSON.stringify({ path: path19, args, format: "json" })
5526
5670
  });
5527
5671
  if (!res.ok) {
5528
- throw new Error(`Convex query ${path20} returned ${res.status}`);
5672
+ throw new Error(`Convex query ${path19} returned ${res.status}`);
5529
5673
  }
5530
5674
  const body = await res.json();
5531
5675
  if (body.status === "error") {
5532
- throw new Error(`Convex query ${path20}: ${body.errorMessage ?? "unknown error"}`);
5676
+ throw new Error(`Convex query ${path19}: ${body.errorMessage ?? "unknown error"}`);
5533
5677
  }
5534
5678
  return body.value;
5535
5679
  }
@@ -5590,7 +5734,7 @@ function isImageExt(filePath) {
5590
5734
  }
5591
5735
  var MAX_IMAGE_SIZE = 5e5;
5592
5736
  function getMarketplaceDir() {
5593
- return path13.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
5737
+ return path12.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
5594
5738
  }
5595
5739
  async function downloadAndExtractZip(slug) {
5596
5740
  const url = `${CLAWHUB_DOWNLOAD_BASE}/download?slug=${encodeURIComponent(slug)}`;
@@ -5628,39 +5772,39 @@ async function downloadAndExtractZip(slug) {
5628
5772
  return files;
5629
5773
  }
5630
5774
  function storeDownloadedSkill(slug, meta, files) {
5631
- const dir = path13.join(getMarketplaceDir(), slug);
5632
- const filesDir = path13.join(dir, "files");
5633
- fs13.mkdirSync(filesDir, { recursive: true });
5775
+ const dir = path12.join(getMarketplaceDir(), slug);
5776
+ const filesDir = path12.join(dir, "files");
5777
+ fs12.mkdirSync(filesDir, { recursive: true });
5634
5778
  const fullMeta = { ...meta, downloadedAt: (/* @__PURE__ */ new Date()).toISOString() };
5635
- fs13.writeFileSync(path13.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
5779
+ fs12.writeFileSync(path12.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
5636
5780
  for (const file of files) {
5637
- const filePath = path13.join(filesDir, file.name);
5638
- fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
5639
- fs13.writeFileSync(filePath, file.content, "utf-8");
5781
+ const filePath = path12.join(filesDir, file.name);
5782
+ fs12.mkdirSync(path12.dirname(filePath), { recursive: true });
5783
+ fs12.writeFileSync(filePath, file.content, "utf-8");
5640
5784
  }
5641
5785
  console.log(`[Marketplace] Stored ${files.length} files for ${slug}`);
5642
5786
  }
5643
5787
  function updateDownloadedAnalysis(slug, analysis) {
5644
- const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
5788
+ const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
5645
5789
  try {
5646
- if (!fs13.existsSync(metaPath)) return;
5647
- const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5790
+ if (!fs12.existsSync(metaPath)) return;
5791
+ const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5648
5792
  meta.analysis = analysis;
5649
- fs13.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
5793
+ fs12.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
5650
5794
  } catch {
5651
5795
  }
5652
5796
  }
5653
5797
  function listDownloadedSkills() {
5654
5798
  const baseDir = getMarketplaceDir();
5655
- if (!fs13.existsSync(baseDir)) return [];
5799
+ if (!fs12.existsSync(baseDir)) return [];
5656
5800
  const results = [];
5657
5801
  try {
5658
- const entries = fs13.readdirSync(baseDir, { withFileTypes: true });
5802
+ const entries = fs12.readdirSync(baseDir, { withFileTypes: true });
5659
5803
  for (const entry of entries) {
5660
5804
  if (!entry.isDirectory()) continue;
5661
- const metaPath = path13.join(baseDir, entry.name, "metadata.json");
5805
+ const metaPath = path12.join(baseDir, entry.name, "metadata.json");
5662
5806
  try {
5663
- const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5807
+ const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5664
5808
  results.push({
5665
5809
  slug: meta.slug,
5666
5810
  name: meta.name,
@@ -5677,17 +5821,17 @@ function listDownloadedSkills() {
5677
5821
  return results;
5678
5822
  }
5679
5823
  function getDownloadedSkillFiles(slug) {
5680
- const filesDir = path13.join(getMarketplaceDir(), slug, "files");
5681
- if (!fs13.existsSync(filesDir)) return [];
5824
+ const filesDir = path12.join(getMarketplaceDir(), slug, "files");
5825
+ if (!fs12.existsSync(filesDir)) return [];
5682
5826
  const files = [];
5683
5827
  function walk(dir, prefix) {
5684
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
5828
+ const entries = fs12.readdirSync(dir, { withFileTypes: true });
5685
5829
  for (const entry of entries) {
5686
5830
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
5687
5831
  if (entry.isDirectory()) {
5688
- walk(path13.join(dir, entry.name), rel);
5832
+ walk(path12.join(dir, entry.name), rel);
5689
5833
  } else {
5690
- const content = fs13.readFileSync(path13.join(dir, entry.name), "utf-8");
5834
+ const content = fs12.readFileSync(path12.join(dir, entry.name), "utf-8");
5691
5835
  files.push({ name: rel, type: guessContentType(entry.name), content });
5692
5836
  }
5693
5837
  }
@@ -5696,10 +5840,10 @@ function getDownloadedSkillFiles(slug) {
5696
5840
  return files;
5697
5841
  }
5698
5842
  function getDownloadedSkillMeta(slug) {
5699
- const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
5843
+ const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
5700
5844
  try {
5701
- if (fs13.existsSync(metaPath)) {
5702
- return JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5845
+ if (fs12.existsSync(metaPath)) {
5846
+ return JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5703
5847
  }
5704
5848
  } catch {
5705
5849
  }
@@ -6236,13 +6380,13 @@ function findSkillMdRecursive(dir, depth = 0) {
6236
6380
  if (depth > 3) return null;
6237
6381
  try {
6238
6382
  for (const name of ["SKILL.md", "skill.md", "README.md", "readme.md"]) {
6239
- const candidate = path14.join(dir, name);
6240
- if (fs14.existsSync(candidate)) return candidate;
6383
+ const candidate = path13.join(dir, name);
6384
+ if (fs13.existsSync(candidate)) return candidate;
6241
6385
  }
6242
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
6386
+ const entries = fs13.readdirSync(dir, { withFileTypes: true });
6243
6387
  for (const entry of entries) {
6244
6388
  if (!entry.isDirectory()) continue;
6245
- const found = findSkillMdRecursive(path14.join(dir, entry.name), depth + 1);
6389
+ const found = findSkillMdRecursive(path13.join(dir, entry.name), depth + 1);
6246
6390
  if (found) return found;
6247
6391
  }
6248
6392
  } catch {
@@ -6253,7 +6397,7 @@ function readSkillDescription(skillDir) {
6253
6397
  try {
6254
6398
  const mdPath = findSkillMdRecursive(skillDir);
6255
6399
  if (!mdPath) return void 0;
6256
- const content = fs14.readFileSync(mdPath, "utf-8");
6400
+ const content = fs13.readFileSync(mdPath, "utf-8");
6257
6401
  const parsed = parseSkillMd(content);
6258
6402
  return parsed?.metadata?.description ?? void 0;
6259
6403
  } catch {
@@ -6272,7 +6416,7 @@ async function skillsRoutes(app) {
6272
6416
  let onDiskNames = [];
6273
6417
  if (skillsDir2) {
6274
6418
  try {
6275
- onDiskNames = fs14.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6419
+ onDiskNames = fs13.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6276
6420
  } catch {
6277
6421
  }
6278
6422
  }
@@ -6285,9 +6429,9 @@ async function skillsRoutes(app) {
6285
6429
  name: a.name,
6286
6430
  source: "user",
6287
6431
  status: "active",
6288
- path: path14.join(skillsDir2 ?? "", a.name),
6432
+ path: path13.join(skillsDir2 ?? "", a.name),
6289
6433
  publisher: a.publisher,
6290
- description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, a.name)) : void 0
6434
+ description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, a.name)) : void 0
6291
6435
  })),
6292
6436
  // Quarantined
6293
6437
  ...quarantined.map((q) => ({
@@ -6302,8 +6446,8 @@ async function skillsRoutes(app) {
6302
6446
  name,
6303
6447
  source: "workspace",
6304
6448
  status: "workspace",
6305
- path: path14.join(skillsDir2 ?? "", name),
6306
- description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, name)) : void 0
6449
+ path: path13.join(skillsDir2 ?? "", name),
6450
+ description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, name)) : void 0
6307
6451
  })),
6308
6452
  // Downloaded (not installed) → available
6309
6453
  ...availableDownloads.map((d) => ({
@@ -6344,20 +6488,20 @@ async function skillsRoutes(app) {
6344
6488
  } else if (entry) {
6345
6489
  source = "user";
6346
6490
  status = "active";
6347
- skillPath = skillsDir2 ? path14.join(skillsDir2, name) : "";
6491
+ skillPath = skillsDir2 ? path13.join(skillsDir2, name) : "";
6348
6492
  } else if (skillsDir2) {
6349
6493
  source = "workspace";
6350
6494
  status = "workspace";
6351
- skillPath = path14.join(skillsDir2, name);
6495
+ skillPath = path13.join(skillsDir2, name);
6352
6496
  }
6353
6497
  let content = "";
6354
6498
  let metadata;
6355
- const dirToRead = skillPath || (skillsDir2 ? path14.join(skillsDir2, name) : "");
6499
+ const dirToRead = skillPath || (skillsDir2 ? path13.join(skillsDir2, name) : "");
6356
6500
  if (dirToRead) {
6357
6501
  try {
6358
6502
  const mdPath = findSkillMdRecursive(dirToRead);
6359
6503
  if (mdPath) {
6360
- content = fs14.readFileSync(mdPath, "utf-8");
6504
+ content = fs13.readFileSync(mdPath, "utf-8");
6361
6505
  const parsed = parseSkillMd(content);
6362
6506
  metadata = parsed?.metadata;
6363
6507
  }
@@ -6486,10 +6630,10 @@ async function skillsRoutes(app) {
6486
6630
  return reply.code(500).send({ error: "Skills directory not configured" });
6487
6631
  }
6488
6632
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
6489
- const binDir = path14.join(agentHome, "bin");
6633
+ const binDir = path13.join(agentHome, "bin");
6490
6634
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
6491
- const skillDir = path14.join(skillsDir2, name);
6492
- const isInstalled = fs14.existsSync(skillDir);
6635
+ const skillDir = path13.join(skillsDir2, name);
6636
+ const isInstalled = fs13.existsSync(skillDir);
6493
6637
  if (isInstalled) {
6494
6638
  try {
6495
6639
  const brokerAvailable = await isBrokerAvailable();
@@ -6499,7 +6643,7 @@ async function skillsRoutes(app) {
6499
6643
  agentHome
6500
6644
  });
6501
6645
  } else {
6502
- fs14.rmSync(skillDir, { recursive: true, force: true });
6646
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6503
6647
  removeSkillWrapper(name, binDir);
6504
6648
  }
6505
6649
  removeSkillEntry(name);
@@ -6533,15 +6677,15 @@ async function skillsRoutes(app) {
6533
6677
  throw new Error("Broker failed to install skill files");
6534
6678
  }
6535
6679
  } else {
6536
- fs14.mkdirSync(skillDir, { recursive: true });
6680
+ fs13.mkdirSync(skillDir, { recursive: true });
6537
6681
  for (const file of files) {
6538
- const filePath = path14.join(skillDir, file.name);
6539
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
6540
- fs14.writeFileSync(filePath, file.content, "utf-8");
6682
+ const filePath = path13.join(skillDir, file.name);
6683
+ fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
6684
+ fs13.writeFileSync(filePath, file.content, "utf-8");
6541
6685
  }
6542
6686
  try {
6543
- execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6544
- execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6687
+ execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6688
+ execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6545
6689
  } catch {
6546
6690
  }
6547
6691
  createSkillWrapper(name, binDir);
@@ -6552,8 +6696,8 @@ async function skillsRoutes(app) {
6552
6696
  return reply.send({ success: true, action: "enabled", name });
6553
6697
  } catch (err) {
6554
6698
  try {
6555
- if (fs14.existsSync(skillDir)) {
6556
- fs14.rmSync(skillDir, { recursive: true, force: true });
6699
+ if (fs13.existsSync(skillDir)) {
6700
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6557
6701
  }
6558
6702
  removeFromApprovedList(name);
6559
6703
  } catch {
@@ -6591,20 +6735,20 @@ async function skillsRoutes(app) {
6591
6735
  return reply.code(500).send({ error: "Skills directory not configured" });
6592
6736
  }
6593
6737
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
6594
- const binDir = path14.join(agentHome, "bin");
6738
+ const binDir = path13.join(agentHome, "bin");
6595
6739
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
6596
- const skillDir = path14.join(skillsDir2, name);
6740
+ const skillDir = path13.join(skillsDir2, name);
6597
6741
  try {
6598
6742
  addToApprovedList(name, publisher);
6599
- fs14.mkdirSync(skillDir, { recursive: true });
6743
+ fs13.mkdirSync(skillDir, { recursive: true });
6600
6744
  for (const file of files) {
6601
- const filePath = path14.join(skillDir, file.name);
6602
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
6603
- fs14.writeFileSync(filePath, file.content, "utf-8");
6745
+ const filePath = path13.join(skillDir, file.name);
6746
+ fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
6747
+ fs13.writeFileSync(filePath, file.content, "utf-8");
6604
6748
  }
6605
6749
  try {
6606
- execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6607
- execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6750
+ execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6751
+ execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6608
6752
  } catch {
6609
6753
  }
6610
6754
  createSkillWrapper(name, binDir);
@@ -6612,8 +6756,8 @@ async function skillsRoutes(app) {
6612
6756
  return reply.send({ success: true, name, analysis });
6613
6757
  } catch (err) {
6614
6758
  try {
6615
- if (fs14.existsSync(skillDir)) {
6616
- fs14.rmSync(skillDir, { recursive: true, force: true });
6759
+ if (fs13.existsSync(skillDir)) {
6760
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6617
6761
  }
6618
6762
  removeFromApprovedList(name);
6619
6763
  } catch {
@@ -6628,8 +6772,8 @@ async function skillsRoutes(app) {
6628
6772
  }
6629
6773
 
6630
6774
  // libs/shield-daemon/src/routes/exec.ts
6631
- import * as fs15 from "node:fs";
6632
- import * as path15 from "node:path";
6775
+ import * as fs14 from "node:fs";
6776
+ import * as path14 from "node:path";
6633
6777
  var ALLOWED_COMMANDS_PATH2 = "/opt/agenshield/config/allowed-commands.json";
6634
6778
  var BIN_DIRS = [
6635
6779
  "/usr/bin",
@@ -6642,22 +6786,22 @@ var binCache = null;
6642
6786
  var BIN_CACHE_TTL = 6e4;
6643
6787
  var VALID_NAME = /^[a-zA-Z0-9_-]+$/;
6644
6788
  function loadConfig2() {
6645
- if (!fs15.existsSync(ALLOWED_COMMANDS_PATH2)) {
6789
+ if (!fs14.existsSync(ALLOWED_COMMANDS_PATH2)) {
6646
6790
  return { version: "1.0.0", commands: [] };
6647
6791
  }
6648
6792
  try {
6649
- const content = fs15.readFileSync(ALLOWED_COMMANDS_PATH2, "utf-8");
6793
+ const content = fs14.readFileSync(ALLOWED_COMMANDS_PATH2, "utf-8");
6650
6794
  return JSON.parse(content);
6651
6795
  } catch {
6652
6796
  return { version: "1.0.0", commands: [] };
6653
6797
  }
6654
6798
  }
6655
6799
  function saveConfig2(config) {
6656
- const dir = path15.dirname(ALLOWED_COMMANDS_PATH2);
6657
- if (!fs15.existsSync(dir)) {
6658
- fs15.mkdirSync(dir, { recursive: true });
6800
+ const dir = path14.dirname(ALLOWED_COMMANDS_PATH2);
6801
+ if (!fs14.existsSync(dir)) {
6802
+ fs14.mkdirSync(dir, { recursive: true });
6659
6803
  }
6660
- fs15.writeFileSync(ALLOWED_COMMANDS_PATH2, JSON.stringify(config, null, 2) + "\n", "utf-8");
6804
+ fs14.writeFileSync(ALLOWED_COMMANDS_PATH2, JSON.stringify(config, null, 2) + "\n", "utf-8");
6661
6805
  }
6662
6806
  function scanSystemBins() {
6663
6807
  const pathDirs = (process.env.PATH ?? "").split(":").filter(Boolean);
@@ -6666,13 +6810,13 @@ function scanSystemBins() {
6666
6810
  const results = [];
6667
6811
  for (const dir of allDirs) {
6668
6812
  try {
6669
- if (!fs15.existsSync(dir)) continue;
6670
- const entries = fs15.readdirSync(dir);
6813
+ if (!fs14.existsSync(dir)) continue;
6814
+ const entries = fs14.readdirSync(dir);
6671
6815
  for (const entry of entries) {
6672
6816
  if (seen.has(entry)) continue;
6673
- const fullPath = path15.join(dir, entry);
6817
+ const fullPath = path14.join(dir, entry);
6674
6818
  try {
6675
- const stat = fs15.statSync(fullPath);
6819
+ const stat = fs14.statSync(fullPath);
6676
6820
  if (stat.isFile() && (stat.mode & 73) !== 0) {
6677
6821
  seen.add(entry);
6678
6822
  results.push({ name: entry, path: fullPath });
@@ -6725,7 +6869,7 @@ async function execRoutes(app) {
6725
6869
  };
6726
6870
  }
6727
6871
  for (const p of paths) {
6728
- if (!path15.isAbsolute(p)) {
6872
+ if (!path14.isAbsolute(p)) {
6729
6873
  return {
6730
6874
  success: false,
6731
6875
  error: {
@@ -7152,8 +7296,8 @@ async function secretsRoutes(app) {
7152
7296
  }
7153
7297
 
7154
7298
  // libs/shield-daemon/src/routes/marketplace.ts
7155
- import * as fs16 from "node:fs";
7156
- import * as path16 from "node:path";
7299
+ import * as fs15 from "node:fs";
7300
+ import * as path15 from "node:path";
7157
7301
  async function marketplaceRoutes(app) {
7158
7302
  app.get(
7159
7303
  "/marketplace/search",
@@ -7349,9 +7493,9 @@ async function marketplaceRoutes(app) {
7349
7493
  return reply.code(500).send({ error: "Skills directory not configured" });
7350
7494
  }
7351
7495
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
7352
- const binDir = path16.join(agentHome, "bin");
7496
+ const binDir = path15.join(agentHome, "bin");
7353
7497
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
7354
- skillDir = path16.join(skillsDir2, slug);
7498
+ skillDir = path15.join(skillsDir2, slug);
7355
7499
  emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
7356
7500
  addToApprovedList(slug, publisher);
7357
7501
  logs.push("Skill pre-approved");
@@ -7394,8 +7538,8 @@ async function marketplaceRoutes(app) {
7394
7538
  });
7395
7539
  } catch (err) {
7396
7540
  try {
7397
- if (skillDir && fs16.existsSync(skillDir)) {
7398
- fs16.rmSync(skillDir, { recursive: true, force: true });
7541
+ if (skillDir && fs15.existsSync(skillDir)) {
7542
+ fs15.rmSync(skillDir, { recursive: true, force: true });
7399
7543
  }
7400
7544
  removeFromApprovedList(slug);
7401
7545
  } catch {
@@ -7418,18 +7562,18 @@ async function marketplaceRoutes(app) {
7418
7562
  }
7419
7563
 
7420
7564
  // libs/shield-daemon/src/routes/fs.ts
7421
- import * as fs17 from "node:fs";
7422
- import * as path17 from "node:path";
7565
+ import * as fs16 from "node:fs";
7566
+ import * as path16 from "node:path";
7423
7567
  import * as os5 from "node:os";
7424
7568
  var MAX_ENTRIES = 200;
7425
7569
  async function fsRoutes(app) {
7426
7570
  app.get("/fs/browse", async (request) => {
7427
7571
  const dirPath = request.query.path || os5.homedir();
7428
7572
  const showHidden = request.query.showHidden === "true";
7429
- const resolvedPath = path17.resolve(dirPath);
7573
+ const resolvedPath = path16.resolve(dirPath);
7430
7574
  let dirents;
7431
7575
  try {
7432
- dirents = fs17.readdirSync(resolvedPath, { withFileTypes: true });
7576
+ dirents = fs16.readdirSync(resolvedPath, { withFileTypes: true });
7433
7577
  } catch {
7434
7578
  return { success: true, data: { entries: [] } };
7435
7579
  }
@@ -7438,7 +7582,7 @@ async function fsRoutes(app) {
7438
7582
  if (!showHidden && dirent.name.startsWith(".")) continue;
7439
7583
  entries.push({
7440
7584
  name: dirent.name,
7441
- path: path17.join(resolvedPath, dirent.name),
7585
+ path: path16.join(resolvedPath, dirent.name),
7442
7586
  type: dirent.isDirectory() ? "directory" : "file"
7443
7587
  });
7444
7588
  if (entries.length >= MAX_ENTRIES) break;
@@ -7452,8 +7596,9 @@ async function fsRoutes(app) {
7452
7596
  }
7453
7597
 
7454
7598
  // libs/shield-daemon/src/services/activity-log.ts
7455
- import * as fs18 from "node:fs";
7456
- import * as path18 from "node:path";
7599
+ import * as fs17 from "node:fs";
7600
+ import * as path17 from "node:path";
7601
+ init_paths();
7457
7602
  var ACTIVITY_FILE = "activity.jsonl";
7458
7603
  var MAX_SIZE_BYTES = 100 * 1024 * 1024;
7459
7604
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -7470,12 +7615,12 @@ var ActivityLog = class {
7470
7615
  writeCount = 0;
7471
7616
  unsubscribe;
7472
7617
  constructor() {
7473
- this.filePath = path18.join(getConfigDir(), ACTIVITY_FILE);
7618
+ this.filePath = path17.join(getConfigDir(), ACTIVITY_FILE);
7474
7619
  }
7475
7620
  /** Read historical events from the JSONL file, newest first */
7476
7621
  getHistory(limit = 500) {
7477
- if (!fs18.existsSync(this.filePath)) return [];
7478
- const content = fs18.readFileSync(this.filePath, "utf-8");
7622
+ if (!fs17.existsSync(this.filePath)) return [];
7623
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7479
7624
  const lines = content.split("\n").filter(Boolean);
7480
7625
  const events = [];
7481
7626
  for (const line of lines) {
@@ -7500,7 +7645,7 @@ var ActivityLog = class {
7500
7645
  }
7501
7646
  append(event) {
7502
7647
  const line = JSON.stringify(event) + "\n";
7503
- fs18.appendFileSync(this.filePath, line, "utf-8");
7648
+ fs17.appendFileSync(this.filePath, line, "utf-8");
7504
7649
  this.writeCount++;
7505
7650
  if (this.writeCount % PRUNE_INTERVAL === 0) {
7506
7651
  this.rotate();
@@ -7508,7 +7653,7 @@ var ActivityLog = class {
7508
7653
  }
7509
7654
  rotate() {
7510
7655
  try {
7511
- const stat = fs18.statSync(this.filePath);
7656
+ const stat = fs17.statSync(this.filePath);
7512
7657
  if (stat.size > MAX_SIZE_BYTES) {
7513
7658
  this.truncateBySize();
7514
7659
  }
@@ -7518,15 +7663,15 @@ var ActivityLog = class {
7518
7663
  }
7519
7664
  /** Keep newest half of lines when file exceeds size limit */
7520
7665
  truncateBySize() {
7521
- const content = fs18.readFileSync(this.filePath, "utf-8");
7666
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7522
7667
  const lines = content.split("\n").filter(Boolean);
7523
7668
  const keep = lines.slice(Math.floor(lines.length / 2));
7524
- fs18.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
7669
+ fs17.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
7525
7670
  }
7526
7671
  /** Remove entries older than 24 hours */
7527
7672
  pruneOldEntries() {
7528
- if (!fs18.existsSync(this.filePath)) return;
7529
- const content = fs18.readFileSync(this.filePath, "utf-8");
7673
+ if (!fs17.existsSync(this.filePath)) return;
7674
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7530
7675
  const lines = content.split("\n").filter(Boolean);
7531
7676
  const cutoff = Date.now() - MAX_AGE_MS;
7532
7677
  const kept = lines.filter((line) => {
@@ -7538,7 +7683,7 @@ var ActivityLog = class {
7538
7683
  }
7539
7684
  });
7540
7685
  if (kept.length < lines.length) {
7541
- fs18.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
7686
+ fs17.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
7542
7687
  }
7543
7688
  }
7544
7689
  };
@@ -7570,26 +7715,31 @@ function globToRegex(pattern) {
7570
7715
  const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{GLOBSTAR}}/g, ".*");
7571
7716
  return new RegExp(`^${regexPattern}$`, "i");
7572
7717
  }
7573
- function normalizeUrlPattern(pattern) {
7718
+ function normalizeUrlBase(pattern) {
7574
7719
  let p = pattern.trim();
7575
7720
  p = p.replace(/\/+$/, "");
7576
7721
  if (!p.match(/^(\*|https?):\/\//i)) {
7577
7722
  p = `https://${p}`;
7578
7723
  }
7579
- if (!p.endsWith("*")) {
7580
- p = `${p}/**`;
7581
- }
7582
7724
  return p;
7583
7725
  }
7726
+ function matchUrlPattern(pattern, target) {
7727
+ const base = normalizeUrlBase(pattern);
7728
+ const trimmed = pattern.trim().replace(/\/+$/, "");
7729
+ if (trimmed.endsWith("*")) {
7730
+ return globToRegex(base).test(target);
7731
+ }
7732
+ return globToRegex(base).test(target) || globToRegex(`${base}/**`).test(target);
7733
+ }
7584
7734
  function normalizeUrlTarget(url) {
7585
7735
  const trimmed = url.trim();
7586
7736
  try {
7587
7737
  const parsed = new URL(trimmed);
7588
- let path20 = parsed.pathname;
7589
- if (path20.length > 1) {
7590
- path20 = path20.replace(/\/+$/, "");
7738
+ let path19 = parsed.pathname;
7739
+ if (path19.length > 1) {
7740
+ path19 = path19.replace(/\/+$/, "");
7591
7741
  }
7592
- return `${parsed.protocol}//${parsed.host}${path20}${parsed.search}`;
7742
+ return `${parsed.protocol}//${parsed.host}${path19}${parsed.search}`;
7593
7743
  } catch {
7594
7744
  return trimmed.replace(/\/+$/, "");
7595
7745
  }
@@ -7621,10 +7771,8 @@ function evaluatePolicyCheck(operation, target) {
7621
7771
  if (policy.target !== "url" || policy.action !== "allow") continue;
7622
7772
  for (const pattern of policy.patterns) {
7623
7773
  if (!pattern.match(/^http:\/\//i)) continue;
7624
- const effectivePattern = normalizeUrlPattern(pattern);
7625
7774
  const effectiveTarget = normalizeUrlTarget(target);
7626
- const regex = globToRegex(effectivePattern);
7627
- if (regex.test(effectiveTarget)) {
7775
+ if (matchUrlPattern(pattern, effectiveTarget)) {
7628
7776
  explicitHttpAllow = true;
7629
7777
  break;
7630
7778
  }
@@ -7646,11 +7794,15 @@ function evaluatePolicyCheck(operation, target) {
7646
7794
  }
7647
7795
  console.log("[policy_check] checking policy:", policy.name, "target:", policy.target, "action:", policy.action);
7648
7796
  for (const pattern of policy.patterns) {
7649
- const effectivePattern = targetType === "url" ? normalizeUrlPattern(pattern) : pattern;
7650
7797
  const effectiveTarget = targetType === "url" ? normalizeUrlTarget(target) : target;
7651
- const regex = globToRegex(effectivePattern);
7652
- const matches = regex.test(effectiveTarget);
7653
- console.log("[policy_check] pattern:", pattern, "-> normalized:", effectivePattern, "| target:", effectiveTarget, "| matches:", matches);
7798
+ let matches;
7799
+ if (targetType === "url") {
7800
+ matches = matchUrlPattern(pattern, effectiveTarget);
7801
+ } else {
7802
+ const regex = globToRegex(pattern);
7803
+ matches = regex.test(effectiveTarget);
7804
+ }
7805
+ console.log("[policy_check] pattern:", pattern, "-> base:", targetType === "url" ? normalizeUrlBase(pattern) : pattern, "| target:", effectiveTarget, "| matches:", matches);
7654
7806
  if (matches) {
7655
7807
  console.log("[policy_check] MATCHED policy:", policy.name, "action:", policy.action);
7656
7808
  return {
@@ -7823,22 +7975,22 @@ async function registerRoutes(app) {
7823
7975
  }
7824
7976
 
7825
7977
  // libs/shield-daemon/src/static.ts
7826
- import * as fs19 from "node:fs";
7827
- import * as path19 from "node:path";
7978
+ import * as fs18 from "node:fs";
7979
+ import * as path18 from "node:path";
7828
7980
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7829
7981
  var __filename = fileURLToPath2(import.meta.url);
7830
- var __dirname = path19.dirname(__filename);
7982
+ var __dirname = path18.dirname(__filename);
7831
7983
  function getUiAssetsPath() {
7832
- const pkgRootPath = path19.join(__dirname, "..", "ui-assets");
7833
- if (fs19.existsSync(pkgRootPath)) {
7984
+ const pkgRootPath = path18.join(__dirname, "..", "ui-assets");
7985
+ if (fs18.existsSync(pkgRootPath)) {
7834
7986
  return pkgRootPath;
7835
7987
  }
7836
- const bundledPath = path19.join(__dirname, "ui-assets");
7837
- if (fs19.existsSync(bundledPath)) {
7988
+ const bundledPath = path18.join(__dirname, "ui-assets");
7989
+ if (fs18.existsSync(bundledPath)) {
7838
7990
  return bundledPath;
7839
7991
  }
7840
- const devPath = path19.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
7841
- if (fs19.existsSync(devPath)) {
7992
+ const devPath = path18.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
7993
+ if (fs18.existsSync(devPath)) {
7842
7994
  return devPath;
7843
7995
  }
7844
7996
  return null;
@@ -7927,8 +8079,8 @@ async function startServer(config) {
7927
8079
  startSecurityWatcher(1e4);
7928
8080
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
7929
8081
  const skillsDir2 = `${agentHome}/.openclaw/skills`;
7930
- if (!fs20.existsSync(skillsDir2)) {
7931
- fs20.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
8082
+ if (!fs19.existsSync(skillsDir2)) {
8083
+ fs19.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
7932
8084
  console.log(`[Daemon] Created skills directory: ${skillsDir2}`);
7933
8085
  }
7934
8086
  startSkillsWatcher(skillsDir2, {
@@ -7937,6 +8089,17 @@ async function startServer(config) {
7937
8089
  }, 3e4);
7938
8090
  const activityLog = getActivityLog();
7939
8091
  activityLog.start();
8092
+ try {
8093
+ const { loadConfig: loadDaemonConfig } = await Promise.resolve().then(() => (init_loader(), loader_exports));
8094
+ const { loadState: loadState2 } = await Promise.resolve().then(() => (init_state(), state_exports));
8095
+ const { syncCommandPoliciesAndWrappers: syncCommandPoliciesAndWrappers2 } = await Promise.resolve().then(() => (init_command_sync(), command_sync_exports));
8096
+ const cfg = loadDaemonConfig();
8097
+ const st = loadState2();
8098
+ syncCommandPoliciesAndWrappers2(cfg.policies, st, app.log);
8099
+ app.log.info("[startup] boot-time command-sync completed");
8100
+ } catch (err) {
8101
+ app.log.warn(`[startup] boot-time command-sync failed: ${err.message}`);
8102
+ }
7940
8103
  try {
7941
8104
  const vault = getVault();
7942
8105
  const agenco = await vault.get("agenco");
@@ -7958,6 +8121,11 @@ async function startServer(config) {
7958
8121
  });
7959
8122
  return app;
7960
8123
  }
8124
+
8125
+ // libs/shield-daemon/src/index.ts
8126
+ init_paths();
8127
+ init_defaults();
8128
+ init_state();
7961
8129
  export {
7962
8130
  VERSION,
7963
8131
  Vault,