@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/acl.d.ts +15 -12
- package/acl.d.ts.map +1 -1
- package/command-sync.d.ts +1 -1
- package/command-sync.d.ts.map +1 -1
- package/index.js +711 -543
- package/main.js +737 -550
- package/package.json +4 -4
- package/routes/config.d.ts.map +1 -1
- package/routes/rpc.d.ts.map +1 -1
- package/server.d.ts.map +1 -1
- package/policy-sync.d.ts +0 -25
- package/policy-sync.d.ts.map +0 -1
package/index.js
CHANGED
|
@@ -1,25 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
240
|
-
import * as
|
|
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
|
|
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 "
|
|
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
|
|
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+
|
|
628
|
-
if (match && match[2] ===
|
|
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,
|
|
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
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
|
984
|
-
if (
|
|
985
|
-
syncFilesystemPolicyAcls(oldPolicies, updated.policies,
|
|
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
|
|
1010
|
-
if (
|
|
1011
|
-
syncFilesystemPolicyAcls(oldConfig.policies, [],
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
1194
|
+
if (!fs6.existsSync(dir)) {
|
|
1055
1195
|
return result;
|
|
1056
1196
|
}
|
|
1057
1197
|
let entries;
|
|
1058
1198
|
try {
|
|
1059
|
-
entries =
|
|
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(
|
|
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 =
|
|
1209
|
+
const ext = path6.extname(entry.name).toLowerCase();
|
|
1070
1210
|
if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
|
|
1071
|
-
const filePath =
|
|
1072
|
-
const relPath =
|
|
1211
|
+
const filePath = path6.join(dir, entry.name);
|
|
1212
|
+
const relPath = path6.relative(root, filePath);
|
|
1073
1213
|
try {
|
|
1074
|
-
result[relPath] =
|
|
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,
|
|
1334
|
+
function emitApiRequest(method, path19, statusCode, duration, requestBody, responseBody) {
|
|
1195
1335
|
daemonEvents.broadcast("api:request", {
|
|
1196
1336
|
method,
|
|
1197
|
-
path:
|
|
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,
|
|
1545
|
+
function isProtectedRoute(method, path19) {
|
|
1405
1546
|
return PROTECTED_ROUTES.some(
|
|
1406
|
-
(route) => route.method === method &&
|
|
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
|
|
1413
|
-
if (!isProtectedRoute(method,
|
|
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
|
|
2246
|
-
import * as
|
|
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
|
|
2312
|
-
import * as
|
|
2313
|
-
import { execSync as
|
|
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 (
|
|
2325
|
-
const content =
|
|
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 =
|
|
2335
|
-
|
|
2478
|
+
const dir = path7.dirname(APPROVED_SKILLS_PATH);
|
|
2479
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
2336
2480
|
const content = JSON.stringify(skills, null, 2);
|
|
2337
|
-
|
|
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 =
|
|
2349
|
-
if (!
|
|
2350
|
-
|
|
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 (
|
|
2353
|
-
|
|
2496
|
+
if (fs7.existsSync(quarantinePath)) {
|
|
2497
|
+
fs7.rmSync(quarantinePath, { recursive: true, force: true });
|
|
2354
2498
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
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 || !
|
|
2516
|
+
if (!skillsDir || !fs7.existsSync(skillsDir)) {
|
|
2373
2517
|
return;
|
|
2374
2518
|
}
|
|
2375
2519
|
try {
|
|
2376
|
-
const entries =
|
|
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 =
|
|
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 (
|
|
2415
|
-
watcher =
|
|
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 =
|
|
2448
|
-
const destPath =
|
|
2449
|
-
if (!
|
|
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
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
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 =
|
|
2475
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
2491
|
-
if (
|
|
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 (!
|
|
2647
|
+
if (!fs7.existsSync(QUARANTINE_DIR)) {
|
|
2504
2648
|
return results;
|
|
2505
2649
|
}
|
|
2506
|
-
const entries =
|
|
2650
|
+
const entries = fs7.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
|
|
2507
2651
|
for (const entry of entries) {
|
|
2508
2652
|
if (entry.isDirectory()) {
|
|
2509
|
-
const stat =
|
|
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:
|
|
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
|
-
|
|
2554
|
-
const entries =
|
|
2697
|
+
fs8.mkdirSync(dest, { recursive: true });
|
|
2698
|
+
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
2555
2699
|
for (const entry of entries) {
|
|
2556
|
-
const srcPath =
|
|
2557
|
-
const destPath =
|
|
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
|
-
|
|
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 =
|
|
2572
|
-
const srcDir =
|
|
2573
|
-
if (
|
|
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 (
|
|
2585
|
-
|
|
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 =
|
|
2601
|
-
const srcDir =
|
|
2602
|
-
if (
|
|
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 (!
|
|
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 (
|
|
2617
|
-
|
|
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
|
|
5141
|
-
import * as
|
|
5142
|
-
import { execSync as
|
|
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
|
|
5147
|
-
import * as
|
|
5148
|
-
import { execSync as
|
|
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 (
|
|
5210
|
-
return JSON.parse(
|
|
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 =
|
|
5219
|
-
if (!
|
|
5220
|
-
|
|
5362
|
+
const dir = path9.dirname(ANALYSIS_CACHE_PATH);
|
|
5363
|
+
if (!fs9.existsSync(dir)) {
|
|
5364
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5221
5365
|
}
|
|
5222
|
-
|
|
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 =
|
|
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
|
|
5365
|
-
import * as
|
|
5366
|
-
import { execSync as
|
|
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 (!
|
|
5369
|
-
|
|
5512
|
+
if (!fs10.existsSync(binDir)) {
|
|
5513
|
+
fs10.mkdirSync(binDir, { recursive: true });
|
|
5370
5514
|
}
|
|
5371
|
-
const wrapperPath =
|
|
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
|
-
|
|
5522
|
+
fs10.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
|
|
5379
5523
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5380
5524
|
try {
|
|
5381
|
-
|
|
5382
|
-
|
|
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 =
|
|
5531
|
+
const wrapperPath = path10.join(binDir, name);
|
|
5388
5532
|
try {
|
|
5389
|
-
if (
|
|
5390
|
-
|
|
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
|
|
5424
|
-
import * as
|
|
5425
|
-
import { execSync as
|
|
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
|
|
5572
|
+
return path11.join(agentHome, ".openclaw", "openclaw.json");
|
|
5429
5573
|
}
|
|
5430
5574
|
function readConfig() {
|
|
5431
5575
|
const configPath = getOpenClawConfigPath();
|
|
5432
5576
|
try {
|
|
5433
|
-
if (
|
|
5434
|
-
return JSON.parse(
|
|
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
|
-
|
|
5444
|
-
|
|
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 =
|
|
5590
|
+
const brokerUser = path11.basename(agentHome) + "_broker";
|
|
5447
5591
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5448
5592
|
try {
|
|
5449
|
-
|
|
5450
|
-
|
|
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
|
|
5480
|
-
import * as
|
|
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(
|
|
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:
|
|
5653
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5510
5654
|
});
|
|
5511
5655
|
if (!res.ok) {
|
|
5512
|
-
throw new Error(`Convex action ${
|
|
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 ${
|
|
5660
|
+
throw new Error(`Convex action ${path19}: ${body.errorMessage ?? "unknown error"}`);
|
|
5517
5661
|
}
|
|
5518
5662
|
return body.value;
|
|
5519
5663
|
}
|
|
5520
|
-
async function convexQuery(
|
|
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:
|
|
5669
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5526
5670
|
});
|
|
5527
5671
|
if (!res.ok) {
|
|
5528
|
-
throw new Error(`Convex query ${
|
|
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 ${
|
|
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
|
|
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 =
|
|
5632
|
-
const filesDir =
|
|
5633
|
-
|
|
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
|
-
|
|
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 =
|
|
5638
|
-
|
|
5639
|
-
|
|
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 =
|
|
5788
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5645
5789
|
try {
|
|
5646
|
-
if (!
|
|
5647
|
-
const meta = JSON.parse(
|
|
5790
|
+
if (!fs12.existsSync(metaPath)) return;
|
|
5791
|
+
const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
|
|
5648
5792
|
meta.analysis = analysis;
|
|
5649
|
-
|
|
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 (!
|
|
5799
|
+
if (!fs12.existsSync(baseDir)) return [];
|
|
5656
5800
|
const results = [];
|
|
5657
5801
|
try {
|
|
5658
|
-
const entries =
|
|
5802
|
+
const entries = fs12.readdirSync(baseDir, { withFileTypes: true });
|
|
5659
5803
|
for (const entry of entries) {
|
|
5660
5804
|
if (!entry.isDirectory()) continue;
|
|
5661
|
-
const metaPath =
|
|
5805
|
+
const metaPath = path12.join(baseDir, entry.name, "metadata.json");
|
|
5662
5806
|
try {
|
|
5663
|
-
const meta = JSON.parse(
|
|
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 =
|
|
5681
|
-
if (!
|
|
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 =
|
|
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(
|
|
5832
|
+
walk(path12.join(dir, entry.name), rel);
|
|
5689
5833
|
} else {
|
|
5690
|
-
const content =
|
|
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 =
|
|
5843
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5700
5844
|
try {
|
|
5701
|
-
if (
|
|
5702
|
-
return JSON.parse(
|
|
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 =
|
|
6240
|
-
if (
|
|
6383
|
+
const candidate = path13.join(dir, name);
|
|
6384
|
+
if (fs13.existsSync(candidate)) return candidate;
|
|
6241
6385
|
}
|
|
6242
|
-
const entries =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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:
|
|
6432
|
+
path: path13.join(skillsDir2 ?? "", a.name),
|
|
6289
6433
|
publisher: a.publisher,
|
|
6290
|
-
description: skillsDir2 ? readSkillDescription(
|
|
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:
|
|
6306
|
-
description: skillsDir2 ? readSkillDescription(
|
|
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 ?
|
|
6491
|
+
skillPath = skillsDir2 ? path13.join(skillsDir2, name) : "";
|
|
6348
6492
|
} else if (skillsDir2) {
|
|
6349
6493
|
source = "workspace";
|
|
6350
6494
|
status = "workspace";
|
|
6351
|
-
skillPath =
|
|
6495
|
+
skillPath = path13.join(skillsDir2, name);
|
|
6352
6496
|
}
|
|
6353
6497
|
let content = "";
|
|
6354
6498
|
let metadata;
|
|
6355
|
-
const dirToRead = skillPath || (skillsDir2 ?
|
|
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 =
|
|
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 =
|
|
6633
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6490
6634
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6491
|
-
const skillDir =
|
|
6492
|
-
const isInstalled =
|
|
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
|
-
|
|
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
|
-
|
|
6680
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6537
6681
|
for (const file of files) {
|
|
6538
|
-
const filePath =
|
|
6539
|
-
|
|
6540
|
-
|
|
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
|
-
|
|
6544
|
-
|
|
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 (
|
|
6556
|
-
|
|
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 =
|
|
6738
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6595
6739
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6596
|
-
const skillDir =
|
|
6740
|
+
const skillDir = path13.join(skillsDir2, name);
|
|
6597
6741
|
try {
|
|
6598
6742
|
addToApprovedList(name, publisher);
|
|
6599
|
-
|
|
6743
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6600
6744
|
for (const file of files) {
|
|
6601
|
-
const filePath =
|
|
6602
|
-
|
|
6603
|
-
|
|
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
|
-
|
|
6607
|
-
|
|
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 (
|
|
6616
|
-
|
|
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
|
|
6632
|
-
import * as
|
|
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 (!
|
|
6789
|
+
if (!fs14.existsSync(ALLOWED_COMMANDS_PATH2)) {
|
|
6646
6790
|
return { version: "1.0.0", commands: [] };
|
|
6647
6791
|
}
|
|
6648
6792
|
try {
|
|
6649
|
-
const content =
|
|
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 =
|
|
6657
|
-
if (!
|
|
6658
|
-
|
|
6800
|
+
const dir = path14.dirname(ALLOWED_COMMANDS_PATH2);
|
|
6801
|
+
if (!fs14.existsSync(dir)) {
|
|
6802
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
6659
6803
|
}
|
|
6660
|
-
|
|
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 (!
|
|
6670
|
-
const entries =
|
|
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 =
|
|
6817
|
+
const fullPath = path14.join(dir, entry);
|
|
6674
6818
|
try {
|
|
6675
|
-
const stat =
|
|
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 (!
|
|
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
|
|
7156
|
-
import * as
|
|
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 =
|
|
7496
|
+
const binDir = path15.join(agentHome, "bin");
|
|
7353
7497
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
7354
|
-
skillDir =
|
|
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 &&
|
|
7398
|
-
|
|
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
|
|
7422
|
-
import * as
|
|
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 =
|
|
7573
|
+
const resolvedPath = path16.resolve(dirPath);
|
|
7430
7574
|
let dirents;
|
|
7431
7575
|
try {
|
|
7432
|
-
dirents =
|
|
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:
|
|
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
|
|
7456
|
-
import * as
|
|
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 =
|
|
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 (!
|
|
7478
|
-
const content =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
7529
|
-
const content =
|
|
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
|
-
|
|
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
|
|
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
|
|
7589
|
-
if (
|
|
7590
|
-
|
|
7738
|
+
let path19 = parsed.pathname;
|
|
7739
|
+
if (path19.length > 1) {
|
|
7740
|
+
path19 = path19.replace(/\/+$/, "");
|
|
7591
7741
|
}
|
|
7592
|
-
return `${parsed.protocol}//${parsed.host}${
|
|
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
|
-
|
|
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
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
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
|
|
7827
|
-
import * as
|
|
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 =
|
|
7982
|
+
var __dirname = path18.dirname(__filename);
|
|
7831
7983
|
function getUiAssetsPath() {
|
|
7832
|
-
const pkgRootPath =
|
|
7833
|
-
if (
|
|
7984
|
+
const pkgRootPath = path18.join(__dirname, "..", "ui-assets");
|
|
7985
|
+
if (fs18.existsSync(pkgRootPath)) {
|
|
7834
7986
|
return pkgRootPath;
|
|
7835
7987
|
}
|
|
7836
|
-
const bundledPath =
|
|
7837
|
-
if (
|
|
7988
|
+
const bundledPath = path18.join(__dirname, "ui-assets");
|
|
7989
|
+
if (fs18.existsSync(bundledPath)) {
|
|
7838
7990
|
return bundledPath;
|
|
7839
7991
|
}
|
|
7840
|
-
const devPath =
|
|
7841
|
-
if (
|
|
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 (!
|
|
7931
|
-
|
|
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,
|