@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/main.js CHANGED
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
-
3
- // libs/shield-daemon/src/main.ts
4
- import * as fs21 from "node:fs";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
5
11
 
6
12
  // libs/shield-daemon/src/config/paths.ts
7
13
  import * as path from "node:path";
@@ -16,10 +22,14 @@ function getConfigPath() {
16
22
  function getPidPath() {
17
23
  return path.join(getConfigDir(), PID_FILE);
18
24
  }
25
+ var init_paths = __esm({
26
+ "libs/shield-daemon/src/config/paths.ts"() {
27
+ "use strict";
28
+ }
29
+ });
19
30
 
20
31
  // libs/shield-daemon/src/config/defaults.ts
21
32
  import { DEFAULT_PORT, DEFAULT_HOST } from "@agenshield/ipc";
22
- var VERSION = "0.1.0";
23
33
  function getDefaultConfig() {
24
34
  return {
25
35
  version: VERSION,
@@ -36,8 +46,22 @@ function getDefaultConfig() {
36
46
  }
37
47
  };
38
48
  }
49
+ var VERSION;
50
+ var init_defaults = __esm({
51
+ "libs/shield-daemon/src/config/defaults.ts"() {
52
+ "use strict";
53
+ VERSION = "0.1.0";
54
+ }
55
+ });
39
56
 
40
57
  // libs/shield-daemon/src/config/loader.ts
58
+ var loader_exports = {};
59
+ __export(loader_exports, {
60
+ ensureConfigDir: () => ensureConfigDir,
61
+ loadConfig: () => loadConfig,
62
+ saveConfig: () => saveConfig,
63
+ updateConfig: () => updateConfig
64
+ });
41
65
  import * as fs from "node:fs";
42
66
  import { ShieldConfigSchema, DEFAULT_PORT as DEFAULT_PORT2 } from "@agenshield/ipc";
43
67
  function ensureConfigDir() {
@@ -84,31 +108,35 @@ function updateConfig(updates) {
84
108
  saveConfig(updated);
85
109
  return updated;
86
110
  }
87
-
88
- // libs/shield-daemon/src/server.ts
89
- import * as fs20 from "node:fs";
90
- import Fastify from "fastify";
91
- import cors from "@fastify/cors";
92
- import fastifyStatic from "@fastify/static";
93
-
94
- // libs/shield-daemon/src/routes/index.ts
95
- import { API_PREFIX } from "@agenshield/ipc";
96
-
97
- // libs/shield-daemon/src/routes/health.ts
98
- async function healthRoutes(app) {
99
- app.get("/health", async () => {
100
- return {
101
- success: true,
102
- data: {
103
- ok: true,
104
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
105
- mode: "daemon"
106
- }
107
- };
108
- });
109
- }
111
+ var init_loader = __esm({
112
+ "libs/shield-daemon/src/config/loader.ts"() {
113
+ "use strict";
114
+ init_paths();
115
+ init_defaults();
116
+ }
117
+ });
110
118
 
111
119
  // libs/shield-daemon/src/state/index.ts
120
+ var state_exports = {};
121
+ __export(state_exports, {
122
+ addConnectedIntegration: () => addConnectedIntegration,
123
+ addGroupState: () => addGroupState,
124
+ addUserState: () => addUserState,
125
+ getDefaultState: () => getDefaultState,
126
+ getPasscodeProtectionState: () => getPasscodeProtectionState,
127
+ getStatePath: () => getStatePath,
128
+ initializeState: () => initializeState,
129
+ loadState: () => loadState,
130
+ removeConnectedIntegration: () => removeConnectedIntegration,
131
+ removeGroupState: () => removeGroupState,
132
+ removeUserState: () => removeUserState,
133
+ saveState: () => saveState,
134
+ updateAgenCoState: () => updateAgenCoState,
135
+ updateDaemonState: () => updateDaemonState,
136
+ updateInstallationState: () => updateInstallationState,
137
+ updatePasscodeProtectionState: () => updatePasscodeProtectionState,
138
+ updateState: () => updateState
139
+ });
112
140
  import * as fs2 from "node:fs";
113
141
  import * as path2 from "node:path";
114
142
  import { STATE_FILE, DEFAULT_PORT as DEFAULT_PORT3 } from "@agenshield/ipc";
@@ -165,18 +193,89 @@ function saveState(state) {
165
193
  }
166
194
  fs2.writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 420 });
167
195
  }
196
+ function updateState(updates) {
197
+ const current = loadState();
198
+ const updated = {
199
+ ...current,
200
+ ...updates
201
+ };
202
+ if (updates.daemon) {
203
+ updated.daemon = { ...current.daemon, ...updates.daemon };
204
+ }
205
+ if (updates.agenco) {
206
+ updated.agenco = { ...current.agenco, ...updates.agenco };
207
+ }
208
+ if (updates.installation) {
209
+ updated.installation = { ...current.installation, ...updates.installation };
210
+ }
211
+ if (updates.passcodeProtection) {
212
+ updated.passcodeProtection = { ...current.passcodeProtection, ...updates.passcodeProtection };
213
+ }
214
+ saveState(updated);
215
+ return updated;
216
+ }
217
+ function updateDaemonState(updates) {
218
+ const current = loadState();
219
+ current.daemon = { ...current.daemon, ...updates };
220
+ saveState(current);
221
+ return current;
222
+ }
168
223
  function updateAgenCoState(updates) {
169
224
  const current = loadState();
170
225
  current.agenco = { ...current.agenco, ...updates };
171
226
  saveState(current);
172
227
  return current;
173
228
  }
229
+ function updateInstallationState(updates) {
230
+ const current = loadState();
231
+ current.installation = { ...current.installation, ...updates };
232
+ saveState(current);
233
+ return current;
234
+ }
174
235
  function updatePasscodeProtectionState(updates) {
175
236
  const current = loadState();
176
237
  current.passcodeProtection = { ...current.passcodeProtection, ...updates };
177
238
  saveState(current);
178
239
  return current;
179
240
  }
241
+ function getPasscodeProtectionState() {
242
+ const current = loadState();
243
+ return current.passcodeProtection;
244
+ }
245
+ function addUserState(user) {
246
+ const current = loadState();
247
+ const existingIndex = current.users.findIndex((u) => u.username === user.username);
248
+ if (existingIndex >= 0) {
249
+ current.users[existingIndex] = user;
250
+ } else {
251
+ current.users.push(user);
252
+ }
253
+ saveState(current);
254
+ return current;
255
+ }
256
+ function removeUserState(username) {
257
+ const current = loadState();
258
+ current.users = current.users.filter((u) => u.username !== username);
259
+ saveState(current);
260
+ return current;
261
+ }
262
+ function addGroupState(group) {
263
+ const current = loadState();
264
+ const existingIndex = current.groups.findIndex((g) => g.name === group.name);
265
+ if (existingIndex >= 0) {
266
+ current.groups[existingIndex] = group;
267
+ } else {
268
+ current.groups.push(group);
269
+ }
270
+ saveState(current);
271
+ return current;
272
+ }
273
+ function removeGroupState(name) {
274
+ const current = loadState();
275
+ current.groups = current.groups.filter((g) => g.name !== name);
276
+ saveState(current);
277
+ return current;
278
+ }
180
279
  function addConnectedIntegration(integrationId) {
181
280
  const current = loadState();
182
281
  if (!current.agenco.connectedIntegrations.includes(integrationId)) {
@@ -185,8 +284,308 @@ function addConnectedIntegration(integrationId) {
185
284
  }
186
285
  return current;
187
286
  }
287
+ function removeConnectedIntegration(integrationId) {
288
+ const current = loadState();
289
+ current.agenco.connectedIntegrations = current.agenco.connectedIntegrations.filter(
290
+ (id) => id !== integrationId
291
+ );
292
+ saveState(current);
293
+ return current;
294
+ }
295
+ function initializeState() {
296
+ const statePath = getStatePath();
297
+ if (!fs2.existsSync(statePath)) {
298
+ const state = getDefaultState();
299
+ saveState(state);
300
+ return state;
301
+ }
302
+ return loadState();
303
+ }
304
+ var init_state = __esm({
305
+ "libs/shield-daemon/src/state/index.ts"() {
306
+ "use strict";
307
+ init_paths();
308
+ }
309
+ });
310
+
311
+ // libs/shield-daemon/src/command-sync.ts
312
+ var command_sync_exports = {};
313
+ __export(command_sync_exports, {
314
+ ensureWrappersInstalled: () => ensureWrappersInstalled,
315
+ syncCommandPolicies: () => syncCommandPolicies,
316
+ syncCommandPoliciesAndWrappers: () => syncCommandPoliciesAndWrappers
317
+ });
318
+ import * as fs5 from "node:fs";
319
+ import * as path5 from "node:path";
320
+ import { execSync as execSync3 } from "node:child_process";
321
+ import { PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS } from "@agenshield/sandbox";
322
+ function resolveCommandPaths(name) {
323
+ const paths = [];
324
+ for (const dir of BIN_SEARCH_DIRS) {
325
+ const candidate = path5.join(dir, name);
326
+ try {
327
+ if (fs5.existsSync(candidate)) {
328
+ const stat = fs5.statSync(candidate);
329
+ if (stat.isFile() && (stat.mode & 73) !== 0) {
330
+ paths.push(candidate);
331
+ }
332
+ }
333
+ } catch {
334
+ }
335
+ }
336
+ if (paths.length === 0) {
337
+ try {
338
+ const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
339
+ if (result && path5.isAbsolute(result)) {
340
+ paths.push(result);
341
+ }
342
+ } catch {
343
+ }
344
+ }
345
+ return paths;
346
+ }
347
+ function extractCommandName(pattern) {
348
+ return pattern.trim().split(/\s+/)[0];
349
+ }
350
+ function extractPolicyCommandNames(policies) {
351
+ const names = /* @__PURE__ */ new Set();
352
+ for (const p of policies) {
353
+ if (p.target === "command" && p.action === "allow" && p.enabled) {
354
+ for (const pattern of p.patterns) {
355
+ const name = extractCommandName(pattern);
356
+ if (name) names.add(name);
357
+ }
358
+ }
359
+ }
360
+ return names;
361
+ }
362
+ function syncCommandPolicies(policies, logger) {
363
+ const log = logger ?? noop2;
364
+ const commandPolicies = policies.filter(
365
+ (p) => p.target === "command" && p.action === "allow" && p.enabled
366
+ );
367
+ const commandNames = /* @__PURE__ */ new Set();
368
+ for (const policy of commandPolicies) {
369
+ for (const pattern of policy.patterns) {
370
+ const name = extractCommandName(pattern);
371
+ if (name) commandNames.add(name);
372
+ }
373
+ }
374
+ const commands = [];
375
+ const now = (/* @__PURE__ */ new Date()).toISOString();
376
+ for (const name of commandNames) {
377
+ const paths = resolveCommandPaths(name);
378
+ if (paths.length === 0) {
379
+ log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
380
+ }
381
+ commands.push({
382
+ name,
383
+ paths,
384
+ addedAt: now,
385
+ addedBy: "policy",
386
+ category: "policy-managed"
387
+ });
388
+ }
389
+ const config = {
390
+ version: "1.0.0",
391
+ commands
392
+ };
393
+ const json = JSON.stringify(config, null, 2) + "\n";
394
+ try {
395
+ const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
396
+ if (!fs5.existsSync(dir)) {
397
+ fs5.mkdirSync(dir, { recursive: true });
398
+ }
399
+ fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
400
+ log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
401
+ } catch {
402
+ log.warn(`[command-sync] cannot write to ${ALLOWED_COMMANDS_PATH} (broker forwards to daemon for policy checks)`);
403
+ }
404
+ }
405
+ function generateFallbackWrapper(cmd) {
406
+ return [
407
+ "#!/bin/bash",
408
+ `# ${cmd} - AgenShield proxy (auto-generated)`,
409
+ "if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi",
410
+ `exec /opt/agenshield/bin/shield-client exec ${cmd} "$@"`,
411
+ ""
412
+ ].join("\n");
413
+ }
414
+ function installWrappersInDir(binDir, log, policyCommands) {
415
+ const shieldExecPath = "/opt/agenshield/bin/shield-exec";
416
+ if (!fs5.existsSync(binDir)) {
417
+ try {
418
+ fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
419
+ } catch {
420
+ log.warn(`[command-sync] cannot create bin dir ${binDir}`);
421
+ return;
422
+ }
423
+ }
424
+ const hasShieldExec = fs5.existsSync(shieldExecPath);
425
+ if (!hasShieldExec) {
426
+ log.warn("[command-sync] shield-exec not found, using bash wrapper fallback");
427
+ }
428
+ for (const cmd of ALL_PROXIED_COMMANDS) {
429
+ const wrapperPath = path5.join(binDir, cmd);
430
+ if (fs5.existsSync(wrapperPath)) {
431
+ continue;
432
+ }
433
+ if (hasShieldExec) {
434
+ try {
435
+ fs5.symlinkSync(shieldExecPath, wrapperPath);
436
+ continue;
437
+ } catch {
438
+ log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
439
+ }
440
+ }
441
+ try {
442
+ fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
443
+ } catch {
444
+ log.warn(`[command-sync] cannot write wrapper for ${cmd}`);
445
+ }
446
+ }
447
+ if (policyCommands) {
448
+ for (const cmd of policyCommands) {
449
+ if (PROXIED_COMMANDS_SET.has(cmd)) continue;
450
+ if (BASIC_SYSTEM_COMMANDS_SET.has(cmd)) continue;
451
+ if (SPECIALIZED_WRAPPER_COMMANDS.has(cmd)) continue;
452
+ const wrapperPath = path5.join(binDir, cmd);
453
+ if (fs5.existsSync(wrapperPath)) continue;
454
+ if (hasShieldExec) {
455
+ try {
456
+ fs5.symlinkSync(shieldExecPath, wrapperPath);
457
+ log.info(`[command-sync] installed dynamic wrapper (symlink): ${cmd}`);
458
+ continue;
459
+ } catch {
460
+ log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
461
+ }
462
+ }
463
+ try {
464
+ fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
465
+ log.info(`[command-sync] installed dynamic wrapper (bash): ${cmd}`);
466
+ } catch {
467
+ log.warn(`[command-sync] cannot write dynamic wrapper for ${cmd}`);
468
+ }
469
+ }
470
+ }
471
+ }
472
+ function cleanupStaleWrappers(binDir, policyCommands, log) {
473
+ const shieldExecPath = "/opt/agenshield/bin/shield-exec";
474
+ let entries;
475
+ try {
476
+ entries = fs5.readdirSync(binDir);
477
+ } catch {
478
+ return;
479
+ }
480
+ for (const entry of entries) {
481
+ if (PROXIED_COMMANDS_SET.has(entry)) continue;
482
+ if (SPECIALIZED_WRAPPER_COMMANDS.has(entry)) continue;
483
+ if (BASIC_SYSTEM_COMMANDS_SET.has(entry)) continue;
484
+ if (policyCommands.has(entry)) continue;
485
+ const wrapperPath = path5.join(binDir, entry);
486
+ try {
487
+ const stat = fs5.lstatSync(wrapperPath);
488
+ if (stat.isSymbolicLink()) {
489
+ const target = fs5.readlinkSync(wrapperPath);
490
+ if (target === shieldExecPath) {
491
+ fs5.unlinkSync(wrapperPath);
492
+ log.info(`[command-sync] removed stale dynamic wrapper (symlink): ${entry}`);
493
+ }
494
+ } else if (stat.isFile()) {
495
+ const content = fs5.readFileSync(wrapperPath, "utf-8");
496
+ if (content.includes("shield-client exec") && content.includes("AgenShield proxy (auto-generated)")) {
497
+ fs5.unlinkSync(wrapperPath);
498
+ log.info(`[command-sync] removed stale dynamic wrapper (bash): ${entry}`);
499
+ }
500
+ }
501
+ } catch {
502
+ }
503
+ }
504
+ }
505
+ function ensureWrappersInstalled(state, logger, policyCommands) {
506
+ const log = logger ?? noop2;
507
+ const agentUser = state.users.find((u) => u.type === "agent");
508
+ if (!agentUser) {
509
+ log.warn("[command-sync] no agent user in state, skipping wrapper installation");
510
+ return;
511
+ }
512
+ const agentBinDir = path5.join(agentUser.homeDir, "bin");
513
+ log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
514
+ installWrappersInDir(agentBinDir, log, policyCommands);
515
+ if (policyCommands) {
516
+ cleanupStaleWrappers(agentBinDir, policyCommands, log);
517
+ }
518
+ const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
519
+ if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
520
+ const envBinDir = path5.join(agentHomeEnv, "bin");
521
+ log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
522
+ installWrappersInDir(envBinDir, log, policyCommands);
523
+ if (policyCommands) {
524
+ cleanupStaleWrappers(envBinDir, policyCommands, log);
525
+ }
526
+ }
527
+ }
528
+ function syncCommandPoliciesAndWrappers(policies, state, logger) {
529
+ const policyCommands = extractPolicyCommandNames(policies);
530
+ syncCommandPolicies(policies, logger);
531
+ ensureWrappersInstalled(state, logger, policyCommands);
532
+ }
533
+ var noop2, ALLOWED_COMMANDS_PATH, BIN_SEARCH_DIRS, ALL_PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS_SET, SPECIALIZED_WRAPPER_COMMANDS, PROXIED_COMMANDS_SET;
534
+ var init_command_sync = __esm({
535
+ "libs/shield-daemon/src/command-sync.ts"() {
536
+ "use strict";
537
+ noop2 = { warn() {
538
+ }, info() {
539
+ } };
540
+ ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
541
+ BIN_SEARCH_DIRS = [
542
+ "/usr/bin",
543
+ "/usr/local/bin",
544
+ "/opt/homebrew/bin",
545
+ "/usr/sbin",
546
+ "/usr/local/sbin",
547
+ "/opt/homebrew/sbin"
548
+ ];
549
+ ALL_PROXIED_COMMANDS = [...PROXIED_COMMANDS];
550
+ BASIC_SYSTEM_COMMANDS_SET = new Set(BASIC_SYSTEM_COMMANDS);
551
+ SPECIALIZED_WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["node", "python", "python3"]);
552
+ PROXIED_COMMANDS_SET = new Set(ALL_PROXIED_COMMANDS);
553
+ }
554
+ });
555
+
556
+ // libs/shield-daemon/src/main.ts
557
+ import * as fs20 from "node:fs";
558
+
559
+ // libs/shield-daemon/src/config/index.ts
560
+ init_paths();
561
+ init_defaults();
562
+ init_loader();
563
+
564
+ // libs/shield-daemon/src/server.ts
565
+ import * as fs19 from "node:fs";
566
+ import Fastify from "fastify";
567
+ import cors from "@fastify/cors";
568
+ import fastifyStatic from "@fastify/static";
569
+
570
+ // libs/shield-daemon/src/routes/index.ts
571
+ import { API_PREFIX } from "@agenshield/ipc";
572
+
573
+ // libs/shield-daemon/src/routes/health.ts
574
+ async function healthRoutes(app) {
575
+ app.get("/health", async () => {
576
+ return {
577
+ success: true,
578
+ data: {
579
+ ok: true,
580
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
581
+ mode: "daemon"
582
+ }
583
+ };
584
+ });
585
+ }
188
586
 
189
587
  // libs/shield-daemon/src/routes/status.ts
588
+ init_state();
190
589
  var startedAt = /* @__PURE__ */ new Date();
191
590
  async function statusRoutes(app) {
192
591
  app.get("/status", async () => {
@@ -213,8 +612,9 @@ async function statusRoutes(app) {
213
612
  }
214
613
 
215
614
  // libs/shield-daemon/src/routes/config.ts
216
- import * as fs7 from "node:fs";
217
- import * as path7 from "node:path";
615
+ import * as fs6 from "node:fs";
616
+ import * as path6 from "node:path";
617
+ init_state();
218
618
 
219
619
  // libs/shield-daemon/src/vault/index.ts
220
620
  import * as fs3 from "node:fs";
@@ -275,6 +675,7 @@ function decrypt(encryptedData, key) {
275
675
  }
276
676
 
277
677
  // libs/shield-daemon/src/vault/index.ts
678
+ init_paths();
278
679
  var Vault = class {
279
680
  key;
280
681
  vaultPath;
@@ -565,21 +966,21 @@ function operationsToAclPerms(operations) {
565
966
  }
566
967
  return perms.join(",");
567
968
  }
568
- function addGroupAcl(targetPath, groupName, permissions, log = noop) {
969
+ function addUserAcl(targetPath, userName, permissions, log = noop) {
569
970
  try {
570
971
  if (!fs4.existsSync(targetPath)) {
571
972
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
572
973
  return;
573
974
  }
574
975
  execSync2(
575
- `sudo chmod +a "group:${groupName} allow ${permissions}" "${targetPath}"`,
976
+ `sudo chmod +a "user:${userName} allow ${permissions}" "${targetPath}"`,
576
977
  { stdio: "pipe" }
577
978
  );
578
979
  } catch (err) {
579
980
  log.warn(`[acl] failed to add ACL on ${targetPath}: ${err.message}`);
580
981
  }
581
982
  }
582
- function removeGroupAcl(targetPath, groupName, log = noop) {
983
+ function removeUserAcl(targetPath, userName, log = noop) {
583
984
  try {
584
985
  if (!fs4.existsSync(targetPath)) {
585
986
  log.warn(`[acl] skipping non-existent path: ${targetPath}`);
@@ -591,8 +992,8 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
591
992
  });
592
993
  const indices = [];
593
994
  for (const line of output.split("\n")) {
594
- const match = line.match(/^\s*(\d+):\s+group:(\S+)\s+allow/);
595
- if (match && match[2] === groupName) {
995
+ const match = line.match(/^\s*(\d+):\s+user:(\S+)\s+/);
996
+ if (match && match[2] === userName) {
596
997
  indices.push(Number(match[1]));
597
998
  }
598
999
  }
@@ -632,7 +1033,7 @@ function mergePerms(a, b) {
632
1033
  }
633
1034
  function computeAclMap(policies) {
634
1035
  const aclMap = /* @__PURE__ */ new Map();
635
- for (const policy of policies) {
1036
+ for (const policy of policies.filter((p) => p.action === "allow")) {
636
1037
  const perms = operationsToAclPerms(policy.operations ?? []);
637
1038
  if (!perms) continue;
638
1039
  for (const pattern of policy.patterns) {
@@ -641,7 +1042,7 @@ function computeAclMap(policies) {
641
1042
  aclMap.set(target, existing ? mergePerms(existing, perms) : perms);
642
1043
  }
643
1044
  }
644
- for (const policy of policies) {
1045
+ for (const policy of policies.filter((p) => p.action === "allow")) {
645
1046
  const perms = operationsToAclPerms(policy.operations ?? []);
646
1047
  if (!perms) continue;
647
1048
  for (const pattern of policy.patterns) {
@@ -655,282 +1056,25 @@ function computeAclMap(policies) {
655
1056
  }
656
1057
  return aclMap;
657
1058
  }
658
- function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
1059
+ function syncFilesystemPolicyAcls(oldPolicies, newPolicies, userName, logger) {
659
1060
  const log = logger ?? noop;
660
1061
  const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
661
1062
  const newFs = newPolicies.filter((p) => p.target === "filesystem");
662
1063
  const oldAclMap = computeAclMap(oldFs);
663
1064
  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
- }
672
- }
673
-
674
- // libs/shield-daemon/src/command-sync.ts
675
- import * as fs5 from "node:fs";
676
- import * as path5 from "node:path";
677
- import { execSync as execSync3 } from "node:child_process";
678
- var noop2 = { warn() {
679
- }, info() {
680
- } };
681
- var ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
682
- var BIN_SEARCH_DIRS = [
683
- "/usr/bin",
684
- "/usr/local/bin",
685
- "/opt/homebrew/bin",
686
- "/usr/sbin",
687
- "/usr/local/sbin",
688
- "/opt/homebrew/sbin"
689
- ];
690
- var ALL_PROXIED_COMMANDS = [
691
- "curl",
692
- "wget",
693
- "git",
694
- "ssh",
695
- "scp",
696
- "rsync",
697
- "brew",
698
- "npm",
699
- "npx",
700
- "pip",
701
- "pip3",
702
- "open-url",
703
- "shieldctl",
704
- "agenco"
705
- ];
706
- function resolveCommandPaths(name) {
707
- const paths = [];
708
- for (const dir of BIN_SEARCH_DIRS) {
709
- const candidate = path5.join(dir, name);
710
- try {
711
- if (fs5.existsSync(candidate)) {
712
- const stat = fs5.statSync(candidate);
713
- if (stat.isFile() && (stat.mode & 73) !== 0) {
714
- paths.push(candidate);
715
- }
716
- }
717
- } catch {
718
- }
719
- }
720
- if (paths.length === 0) {
721
- try {
722
- const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
723
- if (result && path5.isAbsolute(result)) {
724
- paths.push(result);
725
- }
726
- } catch {
727
- }
728
- }
729
- return paths;
730
- }
731
- function extractCommandName(pattern) {
732
- return pattern.trim().split(/\s+/)[0];
733
- }
734
- function syncCommandPolicies(policies, logger) {
735
- const log = logger ?? noop2;
736
- const commandPolicies = policies.filter(
737
- (p) => p.target === "command" && p.action === "allow" && p.enabled
738
- );
739
- const commandNames = /* @__PURE__ */ new Set();
740
- for (const policy of commandPolicies) {
741
- for (const pattern of policy.patterns) {
742
- const name = extractCommandName(pattern);
743
- if (name) commandNames.add(name);
744
- }
745
- }
746
- const commands = [];
747
- const now = (/* @__PURE__ */ new Date()).toISOString();
748
- for (const name of commandNames) {
749
- const paths = resolveCommandPaths(name);
750
- if (paths.length === 0) {
751
- log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
752
- }
753
- commands.push({
754
- name,
755
- paths,
756
- addedAt: now,
757
- addedBy: "policy",
758
- category: "policy-managed"
759
- });
760
- }
761
- const config = {
762
- version: "1.0.0",
763
- commands
764
- };
765
- const json = JSON.stringify(config, null, 2) + "\n";
766
- try {
767
- const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
768
- if (!fs5.existsSync(dir)) {
769
- fs5.mkdirSync(dir, { recursive: true });
770
- }
771
- fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
772
- log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
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
- }
785
- }
786
- }
787
- function installWrappersInDir(binDir, log) {
788
- const shieldExecPath = "/opt/agenshield/bin/shield-exec";
789
- if (!fs5.existsSync(binDir)) {
790
- try {
791
- fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
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
- }
802
- }
803
- }
804
- if (!fs5.existsSync(shieldExecPath)) {
805
- log.warn(`[command-sync] shield-exec not found at ${shieldExecPath}, skipping wrapper installation`);
806
- return;
807
- }
808
- for (const cmd of ALL_PROXIED_COMMANDS) {
809
- const wrapperPath = path5.join(binDir, cmd);
810
- if (fs5.existsSync(wrapperPath)) {
811
- continue;
812
- }
813
- try {
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}`);
823
- }
824
- }
825
- }
826
- }
827
- function ensureWrappersInstalled(state, logger) {
828
- const log = logger ?? noop2;
829
- const agentUser = state.users.find((u) => u.type === "agent");
830
- if (!agentUser) {
831
- log.warn("[command-sync] no agent user in state, skipping wrapper installation");
832
- return;
833
- }
834
- const agentBinDir = path5.join(agentUser.homeDir, "bin");
835
- log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
836
- installWrappersInDir(agentBinDir, log);
837
- const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
838
- if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
839
- const envBinDir = path5.join(agentHomeEnv, "bin");
840
- log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
841
- installWrappersInDir(envBinDir, log);
842
- }
843
- }
844
- function syncCommandPoliciesAndWrappers(policies, state, logger) {
845
- syncCommandPolicies(policies, logger);
846
- ensureWrappersInstalled(state, logger);
847
- }
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}`);
1065
+ const allPaths = /* @__PURE__ */ new Set([...oldAclMap.keys(), ...newAclMap.keys()]);
1066
+ for (const targetPath of allPaths) {
1067
+ removeUserAcl(targetPath, userName, log);
1068
+ const newPerms = newAclMap.get(targetPath);
1069
+ if (newPerms) {
1070
+ addUserAcl(targetPath, userName, newPerms, log);
929
1071
  }
930
1072
  }
931
1073
  }
932
1074
 
933
1075
  // libs/shield-daemon/src/routes/config.ts
1076
+ init_command_sync();
1077
+ import { installShieldExec, createUserConfig } from "@agenshield/sandbox";
934
1078
  async function configRoutes(app) {
935
1079
  app.get("/config", async () => {
936
1080
  const config = loadConfig();
@@ -947,12 +1091,11 @@ async function configRoutes(app) {
947
1091
  const updated = updateConfig(request.body);
948
1092
  if (request.body.policies) {
949
1093
  const state = loadState();
950
- const wsGroup = state.groups.find((g) => g.type === "workspace");
951
- if (wsGroup) {
952
- syncFilesystemPolicyAcls(oldPolicies, updated.policies, wsGroup.name, app.log);
1094
+ const agentUser = state.users.find((u) => u.type === "agent");
1095
+ if (agentUser) {
1096
+ syncFilesystemPolicyAcls(oldPolicies, updated.policies, agentUser.username, app.log);
953
1097
  }
954
1098
  syncCommandPoliciesAndWrappers(updated.policies, state, app.log);
955
- syncPoliciesToBrokerDir(updated.policies, app.log);
956
1099
  }
957
1100
  return {
958
1101
  success: true,
@@ -973,12 +1116,11 @@ async function configRoutes(app) {
973
1116
  try {
974
1117
  const oldConfig = loadConfig();
975
1118
  const state = loadState();
976
- const wsGroup = state.groups.find((g) => g.type === "workspace");
977
- if (wsGroup) {
978
- syncFilesystemPolicyAcls(oldConfig.policies, [], wsGroup.name, app.log);
1119
+ const agentUser = state.users.find((u) => u.type === "agent");
1120
+ if (agentUser) {
1121
+ syncFilesystemPolicyAcls(oldConfig.policies, [], agentUser.username, app.log);
979
1122
  }
980
1123
  syncCommandPoliciesAndWrappers([], state, app.log);
981
- syncPoliciesToBrokerDir([], app.log);
982
1124
  saveConfig(getDefaultConfig());
983
1125
  const vault = getVault();
984
1126
  await vault.destroy();
@@ -994,9 +1136,31 @@ async function configRoutes(app) {
994
1136
  };
995
1137
  }
996
1138
  });
1139
+ app.post("/config/install-wrappers", async () => {
1140
+ try {
1141
+ const state = loadState();
1142
+ const agentUser = state.users.find((u) => u.type === "agent");
1143
+ if (!agentUser) {
1144
+ return { success: false, error: "No agent user found in state" };
1145
+ }
1146
+ const userConfig = createUserConfig();
1147
+ const binDir = path6.join(agentUser.homeDir, "bin");
1148
+ const result = await installShieldExec(userConfig, binDir);
1149
+ return {
1150
+ success: result.success,
1151
+ installed: result.installed,
1152
+ error: result.error
1153
+ };
1154
+ } catch (error) {
1155
+ return {
1156
+ success: false,
1157
+ error: error instanceof Error ? error.message : "Failed to install wrappers"
1158
+ };
1159
+ }
1160
+ });
997
1161
  app.get("/config/openclaw", async (_request, reply) => {
998
1162
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
999
- const configDir = path7.join(agentHome, ".openclaw");
1163
+ const configDir = path6.join(agentHome, ".openclaw");
1000
1164
  const configFiles = readConfigDir(configDir);
1001
1165
  return reply.send({ configDir, files: configFiles });
1002
1166
  });
@@ -1008,7 +1172,7 @@ async function configRoutes(app) {
1008
1172
  return reply.code(400).send({ error: "original query param required" });
1009
1173
  }
1010
1174
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
1011
- const agentConfigDir = path7.join(agentHome, ".openclaw");
1175
+ const agentConfigDir = path6.join(agentHome, ".openclaw");
1012
1176
  const diff = diffConfigDirs(original, agentConfigDir);
1013
1177
  return reply.send({ diff });
1014
1178
  }
@@ -1018,27 +1182,27 @@ var SKIP_DIRS = /* @__PURE__ */ new Set(["skills", "node_modules", ".git", "dist
1018
1182
  function readConfigDir(dir, base) {
1019
1183
  const result = {};
1020
1184
  const root = base ?? dir;
1021
- if (!fs7.existsSync(dir)) {
1185
+ if (!fs6.existsSync(dir)) {
1022
1186
  return result;
1023
1187
  }
1024
1188
  let entries;
1025
1189
  try {
1026
- entries = fs7.readdirSync(dir, { withFileTypes: true });
1190
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
1027
1191
  } catch {
1028
1192
  return result;
1029
1193
  }
1030
1194
  for (const entry of entries) {
1031
1195
  if (entry.isDirectory()) {
1032
1196
  if (SKIP_DIRS.has(entry.name)) continue;
1033
- const sub = readConfigDir(path7.join(dir, entry.name), root);
1197
+ const sub = readConfigDir(path6.join(dir, entry.name), root);
1034
1198
  Object.assign(result, sub);
1035
1199
  } else if (entry.isFile()) {
1036
- const ext = path7.extname(entry.name).toLowerCase();
1200
+ const ext = path6.extname(entry.name).toLowerCase();
1037
1201
  if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
1038
- const filePath = path7.join(dir, entry.name);
1039
- const relPath = path7.relative(root, filePath);
1202
+ const filePath = path6.join(dir, entry.name);
1203
+ const relPath = path6.relative(root, filePath);
1040
1204
  try {
1041
- result[relPath] = fs7.readFileSync(filePath, "utf-8");
1205
+ result[relPath] = fs6.readFileSync(filePath, "utf-8");
1042
1206
  } catch {
1043
1207
  }
1044
1208
  }
@@ -1158,10 +1322,10 @@ function emitSecurityWarning(warning) {
1158
1322
  function emitSecurityCritical(issue) {
1159
1323
  daemonEvents.broadcast("security:critical", { message: issue });
1160
1324
  }
1161
- function emitApiRequest(method, path20, statusCode, duration, requestBody, responseBody) {
1325
+ function emitApiRequest(method, path19, statusCode, duration, requestBody, responseBody) {
1162
1326
  daemonEvents.broadcast("api:request", {
1163
1327
  method,
1164
- path: path20,
1328
+ path: path19,
1165
1329
  statusCode,
1166
1330
  duration,
1167
1331
  ...requestBody !== void 0 && { requestBody },
@@ -1204,6 +1368,7 @@ function emitEvent(type, data) {
1204
1368
 
1205
1369
  // libs/shield-daemon/src/auth/passcode.ts
1206
1370
  import * as crypto3 from "node:crypto";
1371
+ init_state();
1207
1372
  import { DEFAULT_AUTH_CONFIG as DEFAULT_AUTH_CONFIG2 } from "@agenshield/ipc";
1208
1373
  var ITERATIONS = 1e5;
1209
1374
  var KEY_LENGTH = 64;
@@ -1368,16 +1533,16 @@ var PROTECTED_ROUTES = [
1368
1533
  { method: "POST", path: "/api/config/factory-reset" },
1369
1534
  { method: "POST", path: "/api/skills/install" }
1370
1535
  ];
1371
- function isProtectedRoute(method, path20) {
1536
+ function isProtectedRoute(method, path19) {
1372
1537
  return PROTECTED_ROUTES.some(
1373
- (route) => route.method === method && path20.startsWith(route.path)
1538
+ (route) => route.method === method && path19.startsWith(route.path)
1374
1539
  );
1375
1540
  }
1376
1541
  function createAuthHook() {
1377
1542
  return async (request, reply) => {
1378
1543
  const method = request.method;
1379
- const path20 = request.url.split("?")[0];
1380
- if (!isProtectedRoute(method, path20)) {
1544
+ const path19 = request.url.split("?")[0];
1545
+ if (!isProtectedRoute(method, path19)) {
1381
1546
  return;
1382
1547
  }
1383
1548
  if (!isAuthenticated(request)) {
@@ -1884,6 +2049,9 @@ async function loggedFetch(url, init, context) {
1884
2049
  }
1885
2050
  }
1886
2051
 
2052
+ // libs/shield-daemon/src/routes/agenco.ts
2053
+ init_state();
2054
+
1887
2055
  // libs/shield-daemon/src/mcp/client.ts
1888
2056
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
1889
2057
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -2209,8 +2377,8 @@ function getMCPState() {
2209
2377
  }
2210
2378
 
2211
2379
  // libs/shield-daemon/src/services/integration-skills.ts
2212
- import * as fs9 from "node:fs";
2213
- import * as path9 from "node:path";
2380
+ import * as fs8 from "node:fs";
2381
+ import * as path8 from "node:path";
2214
2382
 
2215
2383
  // libs/shield-skills/dist/index.js
2216
2384
  import * as path22 from "node:path";
@@ -2275,9 +2443,9 @@ var __skills_dirname = path22.dirname(fileURLToPath(import.meta.url));
2275
2443
  var BUILTIN_SKILLS_DIR = path22.resolve(__skills_dirname, "..", "skills");
2276
2444
 
2277
2445
  // libs/shield-daemon/src/watchers/skills.ts
2278
- import * as fs8 from "node:fs";
2279
- import * as path8 from "node:path";
2280
- import { execSync as execSync5 } from "node:child_process";
2446
+ import * as fs7 from "node:fs";
2447
+ import * as path7 from "node:path";
2448
+ import { execSync as execSync4 } from "node:child_process";
2281
2449
  var APPROVED_SKILLS_PATH = "/opt/agenshield/config/approved-skills.json";
2282
2450
  var QUARANTINE_DIR = "/opt/agenshield/quarantine/skills";
2283
2451
  var DEBOUNCE_MS = 500;
@@ -2288,8 +2456,8 @@ var skillsDir = "";
2288
2456
  var callbacks = {};
2289
2457
  function loadApprovedSkills() {
2290
2458
  try {
2291
- if (fs8.existsSync(APPROVED_SKILLS_PATH)) {
2292
- const content = fs8.readFileSync(APPROVED_SKILLS_PATH, "utf-8");
2459
+ if (fs7.existsSync(APPROVED_SKILLS_PATH)) {
2460
+ const content = fs7.readFileSync(APPROVED_SKILLS_PATH, "utf-8");
2293
2461
  return JSON.parse(content);
2294
2462
  }
2295
2463
  } catch {
@@ -2298,10 +2466,10 @@ function loadApprovedSkills() {
2298
2466
  }
2299
2467
  function saveApprovedSkills(skills) {
2300
2468
  try {
2301
- const dir = path8.dirname(APPROVED_SKILLS_PATH);
2302
- fs8.mkdirSync(dir, { recursive: true });
2469
+ const dir = path7.dirname(APPROVED_SKILLS_PATH);
2470
+ fs7.mkdirSync(dir, { recursive: true });
2303
2471
  const content = JSON.stringify(skills, null, 2);
2304
- fs8.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
2472
+ fs7.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
2305
2473
  } catch (err) {
2306
2474
  console.error("Failed to save approved skills:", err.message);
2307
2475
  }
@@ -2312,16 +2480,16 @@ function isApproved(skillName) {
2312
2480
  }
2313
2481
  function quarantineSkill(skillName, skillPath) {
2314
2482
  try {
2315
- const quarantinePath = path8.join(QUARANTINE_DIR, skillName);
2316
- if (!fs8.existsSync(QUARANTINE_DIR)) {
2317
- fs8.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
2483
+ const quarantinePath = path7.join(QUARANTINE_DIR, skillName);
2484
+ if (!fs7.existsSync(QUARANTINE_DIR)) {
2485
+ fs7.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
2318
2486
  }
2319
- if (fs8.existsSync(quarantinePath)) {
2320
- fs8.rmSync(quarantinePath, { recursive: true, force: true });
2487
+ if (fs7.existsSync(quarantinePath)) {
2488
+ fs7.rmSync(quarantinePath, { recursive: true, force: true });
2321
2489
  }
2322
- execSync5(`mv "${skillPath}" "${quarantinePath}"`, { stdio: "pipe" });
2323
- execSync5(`chown -R root:wheel "${quarantinePath}"`, { stdio: "pipe" });
2324
- execSync5(`chmod -R 700 "${quarantinePath}"`, { stdio: "pipe" });
2490
+ execSync4(`mv "${skillPath}" "${quarantinePath}"`, { stdio: "pipe" });
2491
+ execSync4(`chown -R root:wheel "${quarantinePath}"`, { stdio: "pipe" });
2492
+ execSync4(`chmod -R 700 "${quarantinePath}"`, { stdio: "pipe" });
2325
2493
  const info = {
2326
2494
  name: skillName,
2327
2495
  quarantinedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2336,16 +2504,16 @@ function quarantineSkill(skillName, skillPath) {
2336
2504
  }
2337
2505
  }
2338
2506
  function scanSkills() {
2339
- if (!skillsDir || !fs8.existsSync(skillsDir)) {
2507
+ if (!skillsDir || !fs7.existsSync(skillsDir)) {
2340
2508
  return;
2341
2509
  }
2342
2510
  try {
2343
- const entries = fs8.readdirSync(skillsDir, { withFileTypes: true });
2511
+ const entries = fs7.readdirSync(skillsDir, { withFileTypes: true });
2344
2512
  for (const entry of entries) {
2345
2513
  if (!entry.isDirectory()) continue;
2346
2514
  const skillName = entry.name;
2347
2515
  if (!isApproved(skillName)) {
2348
- const fullPath = path8.join(skillsDir, skillName);
2516
+ const fullPath = path7.join(skillsDir, skillName);
2349
2517
  const info = quarantineSkill(skillName, fullPath);
2350
2518
  if (info && callbacks.onQuarantined) {
2351
2519
  callbacks.onQuarantined(info);
@@ -2378,8 +2546,8 @@ function startSkillsWatcher(watchDir, cbs = {}, pollIntervalMs = 3e4) {
2378
2546
  callbacks = cbs;
2379
2547
  scanSkills();
2380
2548
  try {
2381
- if (fs8.existsSync(skillsDir)) {
2382
- watcher = fs8.watch(skillsDir, { persistent: false }, handleFsEvent);
2549
+ if (fs7.existsSync(skillsDir)) {
2550
+ watcher = fs7.watch(skillsDir, { persistent: false }, handleFsEvent);
2383
2551
  watcher.on("error", (err) => {
2384
2552
  console.warn("[SkillsWatcher] fs.watch error, falling back to polling:", err.message);
2385
2553
  watcher?.close();
@@ -2411,9 +2579,9 @@ function stopSkillsWatcher() {
2411
2579
  }
2412
2580
  function approveSkill(skillName) {
2413
2581
  try {
2414
- const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
2415
- const destPath = path8.join(skillsDir, skillName);
2416
- if (!fs8.existsSync(quarantinedPath)) {
2582
+ const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
2583
+ const destPath = path7.join(skillsDir, skillName);
2584
+ if (!fs7.existsSync(quarantinedPath)) {
2417
2585
  return { success: false, error: `Skill "${skillName}" not found in quarantine` };
2418
2586
  }
2419
2587
  const approved = loadApprovedSkills();
@@ -2424,9 +2592,9 @@ function approveSkill(skillName) {
2424
2592
  });
2425
2593
  saveApprovedSkills(approved);
2426
2594
  }
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" });
2595
+ execSync4(`mv "${quarantinedPath}" "${destPath}"`, { stdio: "pipe" });
2596
+ execSync4(`chown -R root:wheel "${destPath}"`, { stdio: "pipe" });
2597
+ execSync4(`chmod -R a+rX,go-w "${destPath}"`, { stdio: "pipe" });
2430
2598
  if (callbacks.onApproved) {
2431
2599
  callbacks.onApproved(skillName);
2432
2600
  }
@@ -2438,11 +2606,11 @@ function approveSkill(skillName) {
2438
2606
  }
2439
2607
  function rejectSkill(skillName) {
2440
2608
  try {
2441
- const quarantinedPath = path8.join(QUARANTINE_DIR, skillName);
2442
- if (!fs8.existsSync(quarantinedPath)) {
2609
+ const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
2610
+ if (!fs7.existsSync(quarantinedPath)) {
2443
2611
  return { success: false, error: `Skill "${skillName}" not found in quarantine` };
2444
2612
  }
2445
- fs8.rmSync(quarantinedPath, { recursive: true, force: true });
2613
+ fs7.rmSync(quarantinedPath, { recursive: true, force: true });
2446
2614
  console.log(`[SkillsWatcher] Rejected and deleted skill: ${skillName}`);
2447
2615
  return { success: true };
2448
2616
  } catch (err) {
@@ -2454,8 +2622,8 @@ function revokeSkill(skillName) {
2454
2622
  const approved = loadApprovedSkills();
2455
2623
  const filtered = approved.filter((s) => s.name !== skillName);
2456
2624
  saveApprovedSkills(filtered);
2457
- const skillPath = path8.join(skillsDir, skillName);
2458
- if (fs8.existsSync(skillPath)) {
2625
+ const skillPath = path7.join(skillsDir, skillName);
2626
+ if (fs7.existsSync(skillPath)) {
2459
2627
  quarantineSkill(skillName, skillPath);
2460
2628
  }
2461
2629
  console.log(`[SkillsWatcher] Revoked approval for skill: ${skillName}`);
@@ -2467,17 +2635,17 @@ function revokeSkill(skillName) {
2467
2635
  function listQuarantined() {
2468
2636
  const results = [];
2469
2637
  try {
2470
- if (!fs8.existsSync(QUARANTINE_DIR)) {
2638
+ if (!fs7.existsSync(QUARANTINE_DIR)) {
2471
2639
  return results;
2472
2640
  }
2473
- const entries = fs8.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
2641
+ const entries = fs7.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
2474
2642
  for (const entry of entries) {
2475
2643
  if (entry.isDirectory()) {
2476
- const stat = fs8.statSync(path8.join(QUARANTINE_DIR, entry.name));
2644
+ const stat = fs7.statSync(path7.join(QUARANTINE_DIR, entry.name));
2477
2645
  results.push({
2478
2646
  name: entry.name,
2479
2647
  quarantinedAt: stat.mtime.toISOString(),
2480
- originalPath: path8.join(skillsDir, entry.name),
2648
+ originalPath: path7.join(skillsDir, entry.name),
2481
2649
  reason: "Skill not in approved list"
2482
2650
  });
2483
2651
  }
@@ -2514,15 +2682,15 @@ function removeFromApprovedList(skillName) {
2514
2682
  // libs/shield-daemon/src/services/integration-skills.ts
2515
2683
  var AGENCO_SKILL_NAME = "agenco-secure-integrations";
2516
2684
  function copyDirSync(src, dest) {
2517
- fs9.mkdirSync(dest, { recursive: true });
2518
- const entries = fs9.readdirSync(src, { withFileTypes: true });
2685
+ fs8.mkdirSync(dest, { recursive: true });
2686
+ const entries = fs8.readdirSync(src, { withFileTypes: true });
2519
2687
  for (const entry of entries) {
2520
- const srcPath = path9.join(src, entry.name);
2521
- const destPath = path9.join(dest, entry.name);
2688
+ const srcPath = path8.join(src, entry.name);
2689
+ const destPath = path8.join(dest, entry.name);
2522
2690
  if (entry.isDirectory()) {
2523
2691
  copyDirSync(srcPath, destPath);
2524
2692
  } else {
2525
- fs9.copyFileSync(srcPath, destPath);
2693
+ fs8.copyFileSync(srcPath, destPath);
2526
2694
  }
2527
2695
  }
2528
2696
  }
@@ -2532,9 +2700,9 @@ async function provisionAgenCoSkill() {
2532
2700
  console.warn("[IntegrationSkills] Skills directory not configured \u2014 skipping provision");
2533
2701
  return { installed: false };
2534
2702
  }
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)) {
2703
+ const destDir = path8.join(skillsDir2, AGENCO_SKILL_NAME);
2704
+ const srcDir = path8.join(BUILTIN_SKILLS_DIR, AGENCO_SKILL_NAME);
2705
+ if (fs8.existsSync(destDir)) {
2538
2706
  return { installed: false };
2539
2707
  }
2540
2708
  try {
@@ -2545,8 +2713,8 @@ async function provisionAgenCoSkill() {
2545
2713
  } catch (err) {
2546
2714
  console.error(`[IntegrationSkills] Failed to install "${AGENCO_SKILL_NAME}":`, err.message);
2547
2715
  try {
2548
- if (fs9.existsSync(destDir)) {
2549
- fs9.rmSync(destDir, { recursive: true, force: true });
2716
+ if (fs8.existsSync(destDir)) {
2717
+ fs8.rmSync(destDir, { recursive: true, force: true });
2550
2718
  }
2551
2719
  removeFromApprovedList(AGENCO_SKILL_NAME);
2552
2720
  } catch {
@@ -2561,12 +2729,12 @@ async function provisionIntegrationSkill(integrationSlug) {
2561
2729
  return { installed: false };
2562
2730
  }
2563
2731
  const skillName = `integration-${integrationSlug}`;
2564
- const destDir = path9.join(skillsDir2, skillName);
2565
- const srcDir = path9.join(BUILTIN_SKILLS_DIR, skillName);
2566
- if (fs9.existsSync(destDir)) {
2732
+ const destDir = path8.join(skillsDir2, skillName);
2733
+ const srcDir = path8.join(BUILTIN_SKILLS_DIR, skillName);
2734
+ if (fs8.existsSync(destDir)) {
2567
2735
  return { installed: false };
2568
2736
  }
2569
- if (!fs9.existsSync(srcDir)) {
2737
+ if (!fs8.existsSync(srcDir)) {
2570
2738
  return { installed: false };
2571
2739
  }
2572
2740
  try {
@@ -2577,8 +2745,8 @@ async function provisionIntegrationSkill(integrationSlug) {
2577
2745
  } catch (err) {
2578
2746
  console.error(`[IntegrationSkills] Failed to install "${skillName}":`, err.message);
2579
2747
  try {
2580
- if (fs9.existsSync(destDir)) {
2581
- fs9.rmSync(destDir, { recursive: true, force: true });
2748
+ if (fs8.existsSync(destDir)) {
2749
+ fs8.rmSync(destDir, { recursive: true, force: true });
2582
2750
  }
2583
2751
  removeFromApprovedList(skillName);
2584
2752
  } catch {
@@ -5101,15 +5269,15 @@ async function agencoRoutes(app) {
5101
5269
  }
5102
5270
 
5103
5271
  // libs/shield-daemon/src/routes/skills.ts
5104
- import * as fs14 from "node:fs";
5105
- import * as path14 from "node:path";
5106
- import { execSync as execSync9 } from "node:child_process";
5272
+ import * as fs13 from "node:fs";
5273
+ import * as path13 from "node:path";
5274
+ import { execSync as execSync8 } from "node:child_process";
5107
5275
  import { parseSkillMd } from "@agenshield/sandbox";
5108
5276
 
5109
5277
  // libs/shield-daemon/src/services/skill-analyzer.ts
5110
- import * as fs10 from "node:fs";
5111
- import * as path10 from "node:path";
5112
- import { execSync as execSync6 } from "node:child_process";
5278
+ import * as fs9 from "node:fs";
5279
+ import * as path9 from "node:path";
5280
+ import { execSync as execSync5 } from "node:child_process";
5113
5281
  var ANALYSIS_CACHE_PATH = "/opt/agenshield/config/skill-analyses.json";
5114
5282
  var COMMAND_PATTERNS = [
5115
5283
  // Bash-style: command at start of line or after pipe/semicolon
@@ -5170,8 +5338,8 @@ var IGNORE_WORDS = /* @__PURE__ */ new Set([
5170
5338
  ]);
5171
5339
  function loadCache() {
5172
5340
  try {
5173
- if (fs10.existsSync(ANALYSIS_CACHE_PATH)) {
5174
- return JSON.parse(fs10.readFileSync(ANALYSIS_CACHE_PATH, "utf-8"));
5341
+ if (fs9.existsSync(ANALYSIS_CACHE_PATH)) {
5342
+ return JSON.parse(fs9.readFileSync(ANALYSIS_CACHE_PATH, "utf-8"));
5175
5343
  }
5176
5344
  } catch {
5177
5345
  }
@@ -5179,18 +5347,18 @@ function loadCache() {
5179
5347
  }
5180
5348
  function saveCache(cache3) {
5181
5349
  try {
5182
- const dir = path10.dirname(ANALYSIS_CACHE_PATH);
5183
- if (!fs10.existsSync(dir)) {
5184
- fs10.mkdirSync(dir, { recursive: true });
5350
+ const dir = path9.dirname(ANALYSIS_CACHE_PATH);
5351
+ if (!fs9.existsSync(dir)) {
5352
+ fs9.mkdirSync(dir, { recursive: true });
5185
5353
  }
5186
- fs10.writeFileSync(ANALYSIS_CACHE_PATH, JSON.stringify(cache3, null, 2) + "\n", "utf-8");
5354
+ fs9.writeFileSync(ANALYSIS_CACHE_PATH, JSON.stringify(cache3, null, 2) + "\n", "utf-8");
5187
5355
  } catch (err) {
5188
5356
  console.error("[SkillAnalyzer] Failed to save cache:", err.message);
5189
5357
  }
5190
5358
  }
5191
5359
  function resolveCommand(name) {
5192
5360
  try {
5193
- const result = execSync6(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
5361
+ const result = execSync5(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
5194
5362
  return result || void 0;
5195
5363
  } catch {
5196
5364
  return void 0;
@@ -5325,33 +5493,33 @@ function clearCachedAnalysis(skillName) {
5325
5493
  }
5326
5494
 
5327
5495
  // libs/shield-daemon/src/services/skill-lifecycle.ts
5328
- import * as fs11 from "node:fs";
5329
- import * as path11 from "node:path";
5330
- import { execSync as execSync7 } from "node:child_process";
5496
+ import * as fs10 from "node:fs";
5497
+ import * as path10 from "node:path";
5498
+ import { execSync as execSync6 } from "node:child_process";
5331
5499
  function createSkillWrapper(name, binDir) {
5332
- if (!fs11.existsSync(binDir)) {
5333
- fs11.mkdirSync(binDir, { recursive: true });
5500
+ if (!fs10.existsSync(binDir)) {
5501
+ fs10.mkdirSync(binDir, { recursive: true });
5334
5502
  }
5335
- const wrapperPath = path11.join(binDir, name);
5503
+ const wrapperPath = path10.join(binDir, name);
5336
5504
  const wrapperContent = `#!/bin/bash
5337
5505
  # ${name} skill wrapper - policy-enforced execution
5338
5506
  # Ensure accessible working directory
5339
5507
  if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi
5340
5508
  exec /opt/agenshield/bin/shield-client skill run "${name}" "$@"
5341
5509
  `;
5342
- fs11.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
5510
+ fs10.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
5343
5511
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
5344
5512
  try {
5345
- execSync7(`chown root:${socketGroup} "${wrapperPath}"`, { stdio: "pipe" });
5346
- execSync7(`chmod 755 "${wrapperPath}"`, { stdio: "pipe" });
5513
+ execSync6(`chown root:${socketGroup} "${wrapperPath}"`, { stdio: "pipe" });
5514
+ execSync6(`chmod 755 "${wrapperPath}"`, { stdio: "pipe" });
5347
5515
  } catch {
5348
5516
  }
5349
5517
  }
5350
5518
  function removeSkillWrapper(name, binDir) {
5351
- const wrapperPath = path11.join(binDir, name);
5519
+ const wrapperPath = path10.join(binDir, name);
5352
5520
  try {
5353
- if (fs11.existsSync(wrapperPath)) {
5354
- fs11.unlinkSync(wrapperPath);
5521
+ if (fs10.existsSync(wrapperPath)) {
5522
+ fs10.unlinkSync(wrapperPath);
5355
5523
  }
5356
5524
  } catch {
5357
5525
  }
@@ -5384,18 +5552,18 @@ function removeSkillPolicy(name) {
5384
5552
  }
5385
5553
 
5386
5554
  // libs/shield-daemon/src/services/openclaw-config.ts
5387
- import * as fs12 from "node:fs";
5388
- import * as path12 from "node:path";
5389
- import { execSync as execSync8 } from "node:child_process";
5555
+ import * as fs11 from "node:fs";
5556
+ import * as path11 from "node:path";
5557
+ import { execSync as execSync7 } from "node:child_process";
5390
5558
  function getOpenClawConfigPath() {
5391
5559
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
5392
- return path12.join(agentHome, ".openclaw", "openclaw.json");
5560
+ return path11.join(agentHome, ".openclaw", "openclaw.json");
5393
5561
  }
5394
5562
  function readConfig() {
5395
5563
  const configPath = getOpenClawConfigPath();
5396
5564
  try {
5397
- if (fs12.existsSync(configPath)) {
5398
- return JSON.parse(fs12.readFileSync(configPath, "utf-8"));
5565
+ if (fs11.existsSync(configPath)) {
5566
+ return JSON.parse(fs11.readFileSync(configPath, "utf-8"));
5399
5567
  }
5400
5568
  } catch {
5401
5569
  console.warn("[OpenClawConfig] Failed to read openclaw.json, starting fresh");
@@ -5404,14 +5572,14 @@ function readConfig() {
5404
5572
  }
5405
5573
  function writeConfig(config) {
5406
5574
  const configPath = getOpenClawConfigPath();
5407
- fs12.mkdirSync(path12.dirname(configPath), { recursive: true });
5408
- fs12.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
5575
+ fs11.mkdirSync(path11.dirname(configPath), { recursive: true });
5576
+ fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
5409
5577
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
5410
- const brokerUser = path12.basename(agentHome) + "_broker";
5578
+ const brokerUser = path11.basename(agentHome) + "_broker";
5411
5579
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
5412
5580
  try {
5413
- execSync8(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
5414
- execSync8(`chmod 664 "${configPath}"`, { stdio: "pipe" });
5581
+ execSync7(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
5582
+ execSync7(`chmod 664 "${configPath}"`, { stdio: "pipe" });
5415
5583
  } catch {
5416
5584
  }
5417
5585
  }
@@ -5440,8 +5608,8 @@ function removeSkillEntry(slug) {
5440
5608
  }
5441
5609
 
5442
5610
  // libs/shield-daemon/src/services/marketplace.ts
5443
- import * as fs13 from "node:fs";
5444
- import * as path13 from "node:path";
5611
+ import * as fs12 from "node:fs";
5612
+ import * as path12 from "node:path";
5445
5613
  import * as os4 from "node:os";
5446
5614
  import { CONFIG_DIR as CONFIG_DIR2, MARKETPLACE_DIR } from "@agenshield/ipc";
5447
5615
  var cache = /* @__PURE__ */ new Map();
@@ -5465,35 +5633,35 @@ var ANALYSIS_TIMEOUT = 4 * 6e4;
5465
5633
  var SEARCH_CACHE_TTL = 6e4;
5466
5634
  var DETAIL_CACHE_TTL = 5 * 6e4;
5467
5635
  var SHORT_TIMEOUT = 1e4;
5468
- async function convexAction(path20, args, timeout) {
5636
+ async function convexAction(path19, args, timeout) {
5469
5637
  const res = await fetch(`${CONVEX_BASE}/api/action`, {
5470
5638
  method: "POST",
5471
5639
  signal: AbortSignal.timeout(timeout),
5472
5640
  headers: { "Content-Type": "application/json" },
5473
- body: JSON.stringify({ path: path20, args, format: "json" })
5641
+ body: JSON.stringify({ path: path19, args, format: "json" })
5474
5642
  });
5475
5643
  if (!res.ok) {
5476
- throw new Error(`Convex action ${path20} returned ${res.status}`);
5644
+ throw new Error(`Convex action ${path19} returned ${res.status}`);
5477
5645
  }
5478
5646
  const body = await res.json();
5479
5647
  if (body.status === "error") {
5480
- throw new Error(`Convex action ${path20}: ${body.errorMessage ?? "unknown error"}`);
5648
+ throw new Error(`Convex action ${path19}: ${body.errorMessage ?? "unknown error"}`);
5481
5649
  }
5482
5650
  return body.value;
5483
5651
  }
5484
- async function convexQuery(path20, args, timeout) {
5652
+ async function convexQuery(path19, args, timeout) {
5485
5653
  const res = await fetch(`${CONVEX_BASE}/api/query`, {
5486
5654
  method: "POST",
5487
5655
  signal: AbortSignal.timeout(timeout),
5488
5656
  headers: { "Content-Type": "application/json" },
5489
- body: JSON.stringify({ path: path20, args, format: "json" })
5657
+ body: JSON.stringify({ path: path19, args, format: "json" })
5490
5658
  });
5491
5659
  if (!res.ok) {
5492
- throw new Error(`Convex query ${path20} returned ${res.status}`);
5660
+ throw new Error(`Convex query ${path19} returned ${res.status}`);
5493
5661
  }
5494
5662
  const body = await res.json();
5495
5663
  if (body.status === "error") {
5496
- throw new Error(`Convex query ${path20}: ${body.errorMessage ?? "unknown error"}`);
5664
+ throw new Error(`Convex query ${path19}: ${body.errorMessage ?? "unknown error"}`);
5497
5665
  }
5498
5666
  return body.value;
5499
5667
  }
@@ -5554,7 +5722,7 @@ function isImageExt(filePath) {
5554
5722
  }
5555
5723
  var MAX_IMAGE_SIZE = 5e5;
5556
5724
  function getMarketplaceDir() {
5557
- return path13.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
5725
+ return path12.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
5558
5726
  }
5559
5727
  async function downloadAndExtractZip(slug) {
5560
5728
  const url = `${CLAWHUB_DOWNLOAD_BASE}/download?slug=${encodeURIComponent(slug)}`;
@@ -5592,39 +5760,39 @@ async function downloadAndExtractZip(slug) {
5592
5760
  return files;
5593
5761
  }
5594
5762
  function storeDownloadedSkill(slug, meta, files) {
5595
- const dir = path13.join(getMarketplaceDir(), slug);
5596
- const filesDir = path13.join(dir, "files");
5597
- fs13.mkdirSync(filesDir, { recursive: true });
5763
+ const dir = path12.join(getMarketplaceDir(), slug);
5764
+ const filesDir = path12.join(dir, "files");
5765
+ fs12.mkdirSync(filesDir, { recursive: true });
5598
5766
  const fullMeta = { ...meta, downloadedAt: (/* @__PURE__ */ new Date()).toISOString() };
5599
- fs13.writeFileSync(path13.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
5767
+ fs12.writeFileSync(path12.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
5600
5768
  for (const file of files) {
5601
- const filePath = path13.join(filesDir, file.name);
5602
- fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
5603
- fs13.writeFileSync(filePath, file.content, "utf-8");
5769
+ const filePath = path12.join(filesDir, file.name);
5770
+ fs12.mkdirSync(path12.dirname(filePath), { recursive: true });
5771
+ fs12.writeFileSync(filePath, file.content, "utf-8");
5604
5772
  }
5605
5773
  console.log(`[Marketplace] Stored ${files.length} files for ${slug}`);
5606
5774
  }
5607
5775
  function updateDownloadedAnalysis(slug, analysis) {
5608
- const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
5776
+ const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
5609
5777
  try {
5610
- if (!fs13.existsSync(metaPath)) return;
5611
- const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5778
+ if (!fs12.existsSync(metaPath)) return;
5779
+ const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5612
5780
  meta.analysis = analysis;
5613
- fs13.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
5781
+ fs12.writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
5614
5782
  } catch {
5615
5783
  }
5616
5784
  }
5617
5785
  function listDownloadedSkills() {
5618
5786
  const baseDir = getMarketplaceDir();
5619
- if (!fs13.existsSync(baseDir)) return [];
5787
+ if (!fs12.existsSync(baseDir)) return [];
5620
5788
  const results = [];
5621
5789
  try {
5622
- const entries = fs13.readdirSync(baseDir, { withFileTypes: true });
5790
+ const entries = fs12.readdirSync(baseDir, { withFileTypes: true });
5623
5791
  for (const entry of entries) {
5624
5792
  if (!entry.isDirectory()) continue;
5625
- const metaPath = path13.join(baseDir, entry.name, "metadata.json");
5793
+ const metaPath = path12.join(baseDir, entry.name, "metadata.json");
5626
5794
  try {
5627
- const meta = JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5795
+ const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5628
5796
  results.push({
5629
5797
  slug: meta.slug,
5630
5798
  name: meta.name,
@@ -5641,17 +5809,17 @@ function listDownloadedSkills() {
5641
5809
  return results;
5642
5810
  }
5643
5811
  function getDownloadedSkillFiles(slug) {
5644
- const filesDir = path13.join(getMarketplaceDir(), slug, "files");
5645
- if (!fs13.existsSync(filesDir)) return [];
5812
+ const filesDir = path12.join(getMarketplaceDir(), slug, "files");
5813
+ if (!fs12.existsSync(filesDir)) return [];
5646
5814
  const files = [];
5647
5815
  function walk(dir, prefix) {
5648
- const entries = fs13.readdirSync(dir, { withFileTypes: true });
5816
+ const entries = fs12.readdirSync(dir, { withFileTypes: true });
5649
5817
  for (const entry of entries) {
5650
5818
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
5651
5819
  if (entry.isDirectory()) {
5652
- walk(path13.join(dir, entry.name), rel);
5820
+ walk(path12.join(dir, entry.name), rel);
5653
5821
  } else {
5654
- const content = fs13.readFileSync(path13.join(dir, entry.name), "utf-8");
5822
+ const content = fs12.readFileSync(path12.join(dir, entry.name), "utf-8");
5655
5823
  files.push({ name: rel, type: guessContentType(entry.name), content });
5656
5824
  }
5657
5825
  }
@@ -5660,10 +5828,10 @@ function getDownloadedSkillFiles(slug) {
5660
5828
  return files;
5661
5829
  }
5662
5830
  function getDownloadedSkillMeta(slug) {
5663
- const metaPath = path13.join(getMarketplaceDir(), slug, "metadata.json");
5831
+ const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
5664
5832
  try {
5665
- if (fs13.existsSync(metaPath)) {
5666
- return JSON.parse(fs13.readFileSync(metaPath, "utf-8"));
5833
+ if (fs12.existsSync(metaPath)) {
5834
+ return JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
5667
5835
  }
5668
5836
  } catch {
5669
5837
  }
@@ -6200,13 +6368,13 @@ function findSkillMdRecursive(dir, depth = 0) {
6200
6368
  if (depth > 3) return null;
6201
6369
  try {
6202
6370
  for (const name of ["SKILL.md", "skill.md", "README.md", "readme.md"]) {
6203
- const candidate = path14.join(dir, name);
6204
- if (fs14.existsSync(candidate)) return candidate;
6371
+ const candidate = path13.join(dir, name);
6372
+ if (fs13.existsSync(candidate)) return candidate;
6205
6373
  }
6206
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
6374
+ const entries = fs13.readdirSync(dir, { withFileTypes: true });
6207
6375
  for (const entry of entries) {
6208
6376
  if (!entry.isDirectory()) continue;
6209
- const found = findSkillMdRecursive(path14.join(dir, entry.name), depth + 1);
6377
+ const found = findSkillMdRecursive(path13.join(dir, entry.name), depth + 1);
6210
6378
  if (found) return found;
6211
6379
  }
6212
6380
  } catch {
@@ -6217,7 +6385,7 @@ function readSkillDescription(skillDir) {
6217
6385
  try {
6218
6386
  const mdPath = findSkillMdRecursive(skillDir);
6219
6387
  if (!mdPath) return void 0;
6220
- const content = fs14.readFileSync(mdPath, "utf-8");
6388
+ const content = fs13.readFileSync(mdPath, "utf-8");
6221
6389
  const parsed = parseSkillMd(content);
6222
6390
  return parsed?.metadata?.description ?? void 0;
6223
6391
  } catch {
@@ -6236,7 +6404,7 @@ async function skillsRoutes(app) {
6236
6404
  let onDiskNames = [];
6237
6405
  if (skillsDir2) {
6238
6406
  try {
6239
- onDiskNames = fs14.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6407
+ onDiskNames = fs13.readdirSync(skillsDir2, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6240
6408
  } catch {
6241
6409
  }
6242
6410
  }
@@ -6249,9 +6417,9 @@ async function skillsRoutes(app) {
6249
6417
  name: a.name,
6250
6418
  source: "user",
6251
6419
  status: "active",
6252
- path: path14.join(skillsDir2 ?? "", a.name),
6420
+ path: path13.join(skillsDir2 ?? "", a.name),
6253
6421
  publisher: a.publisher,
6254
- description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, a.name)) : void 0
6422
+ description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, a.name)) : void 0
6255
6423
  })),
6256
6424
  // Quarantined
6257
6425
  ...quarantined.map((q) => ({
@@ -6266,8 +6434,8 @@ async function skillsRoutes(app) {
6266
6434
  name,
6267
6435
  source: "workspace",
6268
6436
  status: "workspace",
6269
- path: path14.join(skillsDir2 ?? "", name),
6270
- description: skillsDir2 ? readSkillDescription(path14.join(skillsDir2, name)) : void 0
6437
+ path: path13.join(skillsDir2 ?? "", name),
6438
+ description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, name)) : void 0
6271
6439
  })),
6272
6440
  // Downloaded (not installed) → available
6273
6441
  ...availableDownloads.map((d) => ({
@@ -6308,20 +6476,20 @@ async function skillsRoutes(app) {
6308
6476
  } else if (entry) {
6309
6477
  source = "user";
6310
6478
  status = "active";
6311
- skillPath = skillsDir2 ? path14.join(skillsDir2, name) : "";
6479
+ skillPath = skillsDir2 ? path13.join(skillsDir2, name) : "";
6312
6480
  } else if (skillsDir2) {
6313
6481
  source = "workspace";
6314
6482
  status = "workspace";
6315
- skillPath = path14.join(skillsDir2, name);
6483
+ skillPath = path13.join(skillsDir2, name);
6316
6484
  }
6317
6485
  let content = "";
6318
6486
  let metadata;
6319
- const dirToRead = skillPath || (skillsDir2 ? path14.join(skillsDir2, name) : "");
6487
+ const dirToRead = skillPath || (skillsDir2 ? path13.join(skillsDir2, name) : "");
6320
6488
  if (dirToRead) {
6321
6489
  try {
6322
6490
  const mdPath = findSkillMdRecursive(dirToRead);
6323
6491
  if (mdPath) {
6324
- content = fs14.readFileSync(mdPath, "utf-8");
6492
+ content = fs13.readFileSync(mdPath, "utf-8");
6325
6493
  const parsed = parseSkillMd(content);
6326
6494
  metadata = parsed?.metadata;
6327
6495
  }
@@ -6450,10 +6618,10 @@ async function skillsRoutes(app) {
6450
6618
  return reply.code(500).send({ error: "Skills directory not configured" });
6451
6619
  }
6452
6620
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
6453
- const binDir = path14.join(agentHome, "bin");
6621
+ const binDir = path13.join(agentHome, "bin");
6454
6622
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
6455
- const skillDir = path14.join(skillsDir2, name);
6456
- const isInstalled = fs14.existsSync(skillDir);
6623
+ const skillDir = path13.join(skillsDir2, name);
6624
+ const isInstalled = fs13.existsSync(skillDir);
6457
6625
  if (isInstalled) {
6458
6626
  try {
6459
6627
  const brokerAvailable = await isBrokerAvailable();
@@ -6463,7 +6631,7 @@ async function skillsRoutes(app) {
6463
6631
  agentHome
6464
6632
  });
6465
6633
  } else {
6466
- fs14.rmSync(skillDir, { recursive: true, force: true });
6634
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6467
6635
  removeSkillWrapper(name, binDir);
6468
6636
  }
6469
6637
  removeSkillEntry(name);
@@ -6497,15 +6665,15 @@ async function skillsRoutes(app) {
6497
6665
  throw new Error("Broker failed to install skill files");
6498
6666
  }
6499
6667
  } else {
6500
- fs14.mkdirSync(skillDir, { recursive: true });
6668
+ fs13.mkdirSync(skillDir, { recursive: true });
6501
6669
  for (const file of files) {
6502
- const filePath = path14.join(skillDir, file.name);
6503
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
6504
- fs14.writeFileSync(filePath, file.content, "utf-8");
6670
+ const filePath = path13.join(skillDir, file.name);
6671
+ fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
6672
+ fs13.writeFileSync(filePath, file.content, "utf-8");
6505
6673
  }
6506
6674
  try {
6507
- execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6508
- execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6675
+ execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6676
+ execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6509
6677
  } catch {
6510
6678
  }
6511
6679
  createSkillWrapper(name, binDir);
@@ -6516,8 +6684,8 @@ async function skillsRoutes(app) {
6516
6684
  return reply.send({ success: true, action: "enabled", name });
6517
6685
  } catch (err) {
6518
6686
  try {
6519
- if (fs14.existsSync(skillDir)) {
6520
- fs14.rmSync(skillDir, { recursive: true, force: true });
6687
+ if (fs13.existsSync(skillDir)) {
6688
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6521
6689
  }
6522
6690
  removeFromApprovedList(name);
6523
6691
  } catch {
@@ -6555,20 +6723,20 @@ async function skillsRoutes(app) {
6555
6723
  return reply.code(500).send({ error: "Skills directory not configured" });
6556
6724
  }
6557
6725
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
6558
- const binDir = path14.join(agentHome, "bin");
6726
+ const binDir = path13.join(agentHome, "bin");
6559
6727
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
6560
- const skillDir = path14.join(skillsDir2, name);
6728
+ const skillDir = path13.join(skillsDir2, name);
6561
6729
  try {
6562
6730
  addToApprovedList(name, publisher);
6563
- fs14.mkdirSync(skillDir, { recursive: true });
6731
+ fs13.mkdirSync(skillDir, { recursive: true });
6564
6732
  for (const file of files) {
6565
- const filePath = path14.join(skillDir, file.name);
6566
- fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
6567
- fs14.writeFileSync(filePath, file.content, "utf-8");
6733
+ const filePath = path13.join(skillDir, file.name);
6734
+ fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
6735
+ fs13.writeFileSync(filePath, file.content, "utf-8");
6568
6736
  }
6569
6737
  try {
6570
- execSync9(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6571
- execSync9(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6738
+ execSync8(`chown -R root:${socketGroup} "${skillDir}"`, { stdio: "pipe" });
6739
+ execSync8(`chmod -R a+rX,go-w "${skillDir}"`, { stdio: "pipe" });
6572
6740
  } catch {
6573
6741
  }
6574
6742
  createSkillWrapper(name, binDir);
@@ -6576,8 +6744,8 @@ async function skillsRoutes(app) {
6576
6744
  return reply.send({ success: true, name, analysis });
6577
6745
  } catch (err) {
6578
6746
  try {
6579
- if (fs14.existsSync(skillDir)) {
6580
- fs14.rmSync(skillDir, { recursive: true, force: true });
6747
+ if (fs13.existsSync(skillDir)) {
6748
+ fs13.rmSync(skillDir, { recursive: true, force: true });
6581
6749
  }
6582
6750
  removeFromApprovedList(name);
6583
6751
  } catch {
@@ -6592,8 +6760,8 @@ async function skillsRoutes(app) {
6592
6760
  }
6593
6761
 
6594
6762
  // libs/shield-daemon/src/routes/exec.ts
6595
- import * as fs15 from "node:fs";
6596
- import * as path15 from "node:path";
6763
+ import * as fs14 from "node:fs";
6764
+ import * as path14 from "node:path";
6597
6765
  var ALLOWED_COMMANDS_PATH2 = "/opt/agenshield/config/allowed-commands.json";
6598
6766
  var BIN_DIRS = [
6599
6767
  "/usr/bin",
@@ -6606,22 +6774,22 @@ var binCache = null;
6606
6774
  var BIN_CACHE_TTL = 6e4;
6607
6775
  var VALID_NAME = /^[a-zA-Z0-9_-]+$/;
6608
6776
  function loadConfig2() {
6609
- if (!fs15.existsSync(ALLOWED_COMMANDS_PATH2)) {
6777
+ if (!fs14.existsSync(ALLOWED_COMMANDS_PATH2)) {
6610
6778
  return { version: "1.0.0", commands: [] };
6611
6779
  }
6612
6780
  try {
6613
- const content = fs15.readFileSync(ALLOWED_COMMANDS_PATH2, "utf-8");
6781
+ const content = fs14.readFileSync(ALLOWED_COMMANDS_PATH2, "utf-8");
6614
6782
  return JSON.parse(content);
6615
6783
  } catch {
6616
6784
  return { version: "1.0.0", commands: [] };
6617
6785
  }
6618
6786
  }
6619
6787
  function saveConfig2(config) {
6620
- const dir = path15.dirname(ALLOWED_COMMANDS_PATH2);
6621
- if (!fs15.existsSync(dir)) {
6622
- fs15.mkdirSync(dir, { recursive: true });
6788
+ const dir = path14.dirname(ALLOWED_COMMANDS_PATH2);
6789
+ if (!fs14.existsSync(dir)) {
6790
+ fs14.mkdirSync(dir, { recursive: true });
6623
6791
  }
6624
- fs15.writeFileSync(ALLOWED_COMMANDS_PATH2, JSON.stringify(config, null, 2) + "\n", "utf-8");
6792
+ fs14.writeFileSync(ALLOWED_COMMANDS_PATH2, JSON.stringify(config, null, 2) + "\n", "utf-8");
6625
6793
  }
6626
6794
  function scanSystemBins() {
6627
6795
  const pathDirs = (process.env.PATH ?? "").split(":").filter(Boolean);
@@ -6630,13 +6798,13 @@ function scanSystemBins() {
6630
6798
  const results = [];
6631
6799
  for (const dir of allDirs) {
6632
6800
  try {
6633
- if (!fs15.existsSync(dir)) continue;
6634
- const entries = fs15.readdirSync(dir);
6801
+ if (!fs14.existsSync(dir)) continue;
6802
+ const entries = fs14.readdirSync(dir);
6635
6803
  for (const entry of entries) {
6636
6804
  if (seen.has(entry)) continue;
6637
- const fullPath = path15.join(dir, entry);
6805
+ const fullPath = path14.join(dir, entry);
6638
6806
  try {
6639
- const stat = fs15.statSync(fullPath);
6807
+ const stat = fs14.statSync(fullPath);
6640
6808
  if (stat.isFile() && (stat.mode & 73) !== 0) {
6641
6809
  seen.add(entry);
6642
6810
  results.push({ name: entry, path: fullPath });
@@ -6689,7 +6857,7 @@ async function execRoutes(app) {
6689
6857
  };
6690
6858
  }
6691
6859
  for (const p of paths) {
6692
- if (!path15.isAbsolute(p)) {
6860
+ if (!path14.isAbsolute(p)) {
6693
6861
  return {
6694
6862
  success: false,
6695
6863
  error: {
@@ -7116,8 +7284,8 @@ async function secretsRoutes(app) {
7116
7284
  }
7117
7285
 
7118
7286
  // libs/shield-daemon/src/routes/marketplace.ts
7119
- import * as fs16 from "node:fs";
7120
- import * as path16 from "node:path";
7287
+ import * as fs15 from "node:fs";
7288
+ import * as path15 from "node:path";
7121
7289
  async function marketplaceRoutes(app) {
7122
7290
  app.get(
7123
7291
  "/marketplace/search",
@@ -7313,9 +7481,9 @@ async function marketplaceRoutes(app) {
7313
7481
  return reply.code(500).send({ error: "Skills directory not configured" });
7314
7482
  }
7315
7483
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
7316
- const binDir = path16.join(agentHome, "bin");
7484
+ const binDir = path15.join(agentHome, "bin");
7317
7485
  const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
7318
- skillDir = path16.join(skillsDir2, slug);
7486
+ skillDir = path15.join(skillsDir2, slug);
7319
7487
  emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
7320
7488
  addToApprovedList(slug, publisher);
7321
7489
  logs.push("Skill pre-approved");
@@ -7358,8 +7526,8 @@ async function marketplaceRoutes(app) {
7358
7526
  });
7359
7527
  } catch (err) {
7360
7528
  try {
7361
- if (skillDir && fs16.existsSync(skillDir)) {
7362
- fs16.rmSync(skillDir, { recursive: true, force: true });
7529
+ if (skillDir && fs15.existsSync(skillDir)) {
7530
+ fs15.rmSync(skillDir, { recursive: true, force: true });
7363
7531
  }
7364
7532
  removeFromApprovedList(slug);
7365
7533
  } catch {
@@ -7382,18 +7550,18 @@ async function marketplaceRoutes(app) {
7382
7550
  }
7383
7551
 
7384
7552
  // libs/shield-daemon/src/routes/fs.ts
7385
- import * as fs17 from "node:fs";
7386
- import * as path17 from "node:path";
7553
+ import * as fs16 from "node:fs";
7554
+ import * as path16 from "node:path";
7387
7555
  import * as os5 from "node:os";
7388
7556
  var MAX_ENTRIES = 200;
7389
7557
  async function fsRoutes(app) {
7390
7558
  app.get("/fs/browse", async (request) => {
7391
7559
  const dirPath = request.query.path || os5.homedir();
7392
7560
  const showHidden = request.query.showHidden === "true";
7393
- const resolvedPath = path17.resolve(dirPath);
7561
+ const resolvedPath = path16.resolve(dirPath);
7394
7562
  let dirents;
7395
7563
  try {
7396
- dirents = fs17.readdirSync(resolvedPath, { withFileTypes: true });
7564
+ dirents = fs16.readdirSync(resolvedPath, { withFileTypes: true });
7397
7565
  } catch {
7398
7566
  return { success: true, data: { entries: [] } };
7399
7567
  }
@@ -7402,7 +7570,7 @@ async function fsRoutes(app) {
7402
7570
  if (!showHidden && dirent.name.startsWith(".")) continue;
7403
7571
  entries.push({
7404
7572
  name: dirent.name,
7405
- path: path17.join(resolvedPath, dirent.name),
7573
+ path: path16.join(resolvedPath, dirent.name),
7406
7574
  type: dirent.isDirectory() ? "directory" : "file"
7407
7575
  });
7408
7576
  if (entries.length >= MAX_ENTRIES) break;
@@ -7416,8 +7584,9 @@ async function fsRoutes(app) {
7416
7584
  }
7417
7585
 
7418
7586
  // libs/shield-daemon/src/services/activity-log.ts
7419
- import * as fs18 from "node:fs";
7420
- import * as path18 from "node:path";
7587
+ import * as fs17 from "node:fs";
7588
+ import * as path17 from "node:path";
7589
+ init_paths();
7421
7590
  var ACTIVITY_FILE = "activity.jsonl";
7422
7591
  var MAX_SIZE_BYTES = 100 * 1024 * 1024;
7423
7592
  var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
@@ -7434,12 +7603,12 @@ var ActivityLog = class {
7434
7603
  writeCount = 0;
7435
7604
  unsubscribe;
7436
7605
  constructor() {
7437
- this.filePath = path18.join(getConfigDir(), ACTIVITY_FILE);
7606
+ this.filePath = path17.join(getConfigDir(), ACTIVITY_FILE);
7438
7607
  }
7439
7608
  /** Read historical events from the JSONL file, newest first */
7440
7609
  getHistory(limit = 500) {
7441
- if (!fs18.existsSync(this.filePath)) return [];
7442
- const content = fs18.readFileSync(this.filePath, "utf-8");
7610
+ if (!fs17.existsSync(this.filePath)) return [];
7611
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7443
7612
  const lines = content.split("\n").filter(Boolean);
7444
7613
  const events = [];
7445
7614
  for (const line of lines) {
@@ -7464,7 +7633,7 @@ var ActivityLog = class {
7464
7633
  }
7465
7634
  append(event) {
7466
7635
  const line = JSON.stringify(event) + "\n";
7467
- fs18.appendFileSync(this.filePath, line, "utf-8");
7636
+ fs17.appendFileSync(this.filePath, line, "utf-8");
7468
7637
  this.writeCount++;
7469
7638
  if (this.writeCount % PRUNE_INTERVAL === 0) {
7470
7639
  this.rotate();
@@ -7472,7 +7641,7 @@ var ActivityLog = class {
7472
7641
  }
7473
7642
  rotate() {
7474
7643
  try {
7475
- const stat = fs18.statSync(this.filePath);
7644
+ const stat = fs17.statSync(this.filePath);
7476
7645
  if (stat.size > MAX_SIZE_BYTES) {
7477
7646
  this.truncateBySize();
7478
7647
  }
@@ -7482,15 +7651,15 @@ var ActivityLog = class {
7482
7651
  }
7483
7652
  /** Keep newest half of lines when file exceeds size limit */
7484
7653
  truncateBySize() {
7485
- const content = fs18.readFileSync(this.filePath, "utf-8");
7654
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7486
7655
  const lines = content.split("\n").filter(Boolean);
7487
7656
  const keep = lines.slice(Math.floor(lines.length / 2));
7488
- fs18.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
7657
+ fs17.writeFileSync(this.filePath, keep.join("\n") + "\n", "utf-8");
7489
7658
  }
7490
7659
  /** Remove entries older than 24 hours */
7491
7660
  pruneOldEntries() {
7492
- if (!fs18.existsSync(this.filePath)) return;
7493
- const content = fs18.readFileSync(this.filePath, "utf-8");
7661
+ if (!fs17.existsSync(this.filePath)) return;
7662
+ const content = fs17.readFileSync(this.filePath, "utf-8");
7494
7663
  const lines = content.split("\n").filter(Boolean);
7495
7664
  const cutoff = Date.now() - MAX_AGE_MS;
7496
7665
  const kept = lines.filter((line) => {
@@ -7502,7 +7671,7 @@ var ActivityLog = class {
7502
7671
  }
7503
7672
  });
7504
7673
  if (kept.length < lines.length) {
7505
- fs18.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
7674
+ fs17.writeFileSync(this.filePath, kept.join("\n") + "\n", "utf-8");
7506
7675
  }
7507
7676
  }
7508
7677
  };
@@ -7534,26 +7703,31 @@ function globToRegex(pattern) {
7534
7703
  const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{GLOBSTAR}}/g, ".*");
7535
7704
  return new RegExp(`^${regexPattern}$`, "i");
7536
7705
  }
7537
- function normalizeUrlPattern(pattern) {
7706
+ function normalizeUrlBase(pattern) {
7538
7707
  let p = pattern.trim();
7539
7708
  p = p.replace(/\/+$/, "");
7540
7709
  if (!p.match(/^(\*|https?):\/\//i)) {
7541
7710
  p = `https://${p}`;
7542
7711
  }
7543
- if (!p.endsWith("*")) {
7544
- p = `${p}/**`;
7545
- }
7546
7712
  return p;
7547
7713
  }
7714
+ function matchUrlPattern(pattern, target) {
7715
+ const base = normalizeUrlBase(pattern);
7716
+ const trimmed = pattern.trim().replace(/\/+$/, "");
7717
+ if (trimmed.endsWith("*")) {
7718
+ return globToRegex(base).test(target);
7719
+ }
7720
+ return globToRegex(base).test(target) || globToRegex(`${base}/**`).test(target);
7721
+ }
7548
7722
  function normalizeUrlTarget(url) {
7549
7723
  const trimmed = url.trim();
7550
7724
  try {
7551
7725
  const parsed = new URL(trimmed);
7552
- let path20 = parsed.pathname;
7553
- if (path20.length > 1) {
7554
- path20 = path20.replace(/\/+$/, "");
7726
+ let path19 = parsed.pathname;
7727
+ if (path19.length > 1) {
7728
+ path19 = path19.replace(/\/+$/, "");
7555
7729
  }
7556
- return `${parsed.protocol}//${parsed.host}${path20}${parsed.search}`;
7730
+ return `${parsed.protocol}//${parsed.host}${path19}${parsed.search}`;
7557
7731
  } catch {
7558
7732
  return trimmed.replace(/\/+$/, "");
7559
7733
  }
@@ -7585,10 +7759,8 @@ function evaluatePolicyCheck(operation, target) {
7585
7759
  if (policy.target !== "url" || policy.action !== "allow") continue;
7586
7760
  for (const pattern of policy.patterns) {
7587
7761
  if (!pattern.match(/^http:\/\//i)) continue;
7588
- const effectivePattern = normalizeUrlPattern(pattern);
7589
7762
  const effectiveTarget = normalizeUrlTarget(target);
7590
- const regex = globToRegex(effectivePattern);
7591
- if (regex.test(effectiveTarget)) {
7763
+ if (matchUrlPattern(pattern, effectiveTarget)) {
7592
7764
  explicitHttpAllow = true;
7593
7765
  break;
7594
7766
  }
@@ -7610,11 +7782,15 @@ function evaluatePolicyCheck(operation, target) {
7610
7782
  }
7611
7783
  console.log("[policy_check] checking policy:", policy.name, "target:", policy.target, "action:", policy.action);
7612
7784
  for (const pattern of policy.patterns) {
7613
- const effectivePattern = targetType === "url" ? normalizeUrlPattern(pattern) : pattern;
7614
7785
  const effectiveTarget = targetType === "url" ? normalizeUrlTarget(target) : target;
7615
- const regex = globToRegex(effectivePattern);
7616
- const matches = regex.test(effectiveTarget);
7617
- console.log("[policy_check] pattern:", pattern, "-> normalized:", effectivePattern, "| target:", effectiveTarget, "| matches:", matches);
7786
+ let matches;
7787
+ if (targetType === "url") {
7788
+ matches = matchUrlPattern(pattern, effectiveTarget);
7789
+ } else {
7790
+ const regex = globToRegex(pattern);
7791
+ matches = regex.test(effectiveTarget);
7792
+ }
7793
+ console.log("[policy_check] pattern:", pattern, "-> base:", targetType === "url" ? normalizeUrlBase(pattern) : pattern, "| target:", effectiveTarget, "| matches:", matches);
7618
7794
  if (matches) {
7619
7795
  console.log("[policy_check] MATCHED policy:", policy.name, "action:", policy.action);
7620
7796
  return {
@@ -7787,22 +7963,22 @@ async function registerRoutes(app) {
7787
7963
  }
7788
7964
 
7789
7965
  // libs/shield-daemon/src/static.ts
7790
- import * as fs19 from "node:fs";
7791
- import * as path19 from "node:path";
7966
+ import * as fs18 from "node:fs";
7967
+ import * as path18 from "node:path";
7792
7968
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7793
7969
  var __filename = fileURLToPath2(import.meta.url);
7794
- var __dirname = path19.dirname(__filename);
7970
+ var __dirname = path18.dirname(__filename);
7795
7971
  function getUiAssetsPath() {
7796
- const pkgRootPath = path19.join(__dirname, "..", "ui-assets");
7797
- if (fs19.existsSync(pkgRootPath)) {
7972
+ const pkgRootPath = path18.join(__dirname, "..", "ui-assets");
7973
+ if (fs18.existsSync(pkgRootPath)) {
7798
7974
  return pkgRootPath;
7799
7975
  }
7800
- const bundledPath = path19.join(__dirname, "ui-assets");
7801
- if (fs19.existsSync(bundledPath)) {
7976
+ const bundledPath = path18.join(__dirname, "ui-assets");
7977
+ if (fs18.existsSync(bundledPath)) {
7802
7978
  return bundledPath;
7803
7979
  }
7804
- const devPath = path19.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
7805
- if (fs19.existsSync(devPath)) {
7980
+ const devPath = path18.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
7981
+ if (fs18.existsSync(devPath)) {
7806
7982
  return devPath;
7807
7983
  }
7808
7984
  return null;
@@ -7891,8 +8067,8 @@ async function startServer(config) {
7891
8067
  startSecurityWatcher(1e4);
7892
8068
  const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
7893
8069
  const skillsDir2 = `${agentHome}/.openclaw/skills`;
7894
- if (!fs20.existsSync(skillsDir2)) {
7895
- fs20.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
8070
+ if (!fs19.existsSync(skillsDir2)) {
8071
+ fs19.mkdirSync(skillsDir2, { recursive: true, mode: 493 });
7896
8072
  console.log(`[Daemon] Created skills directory: ${skillsDir2}`);
7897
8073
  }
7898
8074
  startSkillsWatcher(skillsDir2, {
@@ -7901,6 +8077,17 @@ async function startServer(config) {
7901
8077
  }, 3e4);
7902
8078
  const activityLog = getActivityLog();
7903
8079
  activityLog.start();
8080
+ try {
8081
+ const { loadConfig: loadDaemonConfig } = await Promise.resolve().then(() => (init_loader(), loader_exports));
8082
+ const { loadState: loadState2 } = await Promise.resolve().then(() => (init_state(), state_exports));
8083
+ const { syncCommandPoliciesAndWrappers: syncCommandPoliciesAndWrappers2 } = await Promise.resolve().then(() => (init_command_sync(), command_sync_exports));
8084
+ const cfg = loadDaemonConfig();
8085
+ const st = loadState2();
8086
+ syncCommandPoliciesAndWrappers2(cfg.policies, st, app.log);
8087
+ app.log.info("[startup] boot-time command-sync completed");
8088
+ } catch (err) {
8089
+ app.log.warn(`[startup] boot-time command-sync failed: ${err.message}`);
8090
+ }
7904
8091
  try {
7905
8092
  const vault = getVault();
7906
8093
  const agenco = await vault.get("agenco");
@@ -7928,10 +8115,10 @@ async function main() {
7928
8115
  ensureConfigDir();
7929
8116
  const config = loadConfig();
7930
8117
  const pidPath = getPidPath();
7931
- fs21.writeFileSync(pidPath, process.pid.toString(), "utf-8");
8118
+ fs20.writeFileSync(pidPath, process.pid.toString(), "utf-8");
7932
8119
  const cleanup = () => {
7933
- if (fs21.existsSync(pidPath)) {
7934
- fs21.unlinkSync(pidPath);
8120
+ if (fs20.existsSync(pidPath)) {
8121
+ fs20.unlinkSync(pidPath);
7935
8122
  }
7936
8123
  process.exit(0);
7937
8124
  };