@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/main.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
|
217
|
-
import * as
|
|
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
|
|
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 "
|
|
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
|
|
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+
|
|
595
|
-
if (match && match[2] ===
|
|
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,
|
|
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
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
|
951
|
-
if (
|
|
952
|
-
syncFilesystemPolicyAcls(oldPolicies, updated.policies,
|
|
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
|
|
977
|
-
if (
|
|
978
|
-
syncFilesystemPolicyAcls(oldConfig.policies, [],
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
1185
|
+
if (!fs6.existsSync(dir)) {
|
|
1022
1186
|
return result;
|
|
1023
1187
|
}
|
|
1024
1188
|
let entries;
|
|
1025
1189
|
try {
|
|
1026
|
-
entries =
|
|
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(
|
|
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 =
|
|
1200
|
+
const ext = path6.extname(entry.name).toLowerCase();
|
|
1037
1201
|
if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
|
|
1038
|
-
const filePath =
|
|
1039
|
-
const relPath =
|
|
1202
|
+
const filePath = path6.join(dir, entry.name);
|
|
1203
|
+
const relPath = path6.relative(root, filePath);
|
|
1040
1204
|
try {
|
|
1041
|
-
result[relPath] =
|
|
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,
|
|
1325
|
+
function emitApiRequest(method, path19, statusCode, duration, requestBody, responseBody) {
|
|
1162
1326
|
daemonEvents.broadcast("api:request", {
|
|
1163
1327
|
method,
|
|
1164
|
-
path:
|
|
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,
|
|
1536
|
+
function isProtectedRoute(method, path19) {
|
|
1372
1537
|
return PROTECTED_ROUTES.some(
|
|
1373
|
-
(route) => route.method === method &&
|
|
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
|
|
1380
|
-
if (!isProtectedRoute(method,
|
|
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
|
|
2213
|
-
import * as
|
|
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
|
|
2279
|
-
import * as
|
|
2280
|
-
import { execSync as
|
|
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 (
|
|
2292
|
-
const content =
|
|
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 =
|
|
2302
|
-
|
|
2469
|
+
const dir = path7.dirname(APPROVED_SKILLS_PATH);
|
|
2470
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
2303
2471
|
const content = JSON.stringify(skills, null, 2);
|
|
2304
|
-
|
|
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 =
|
|
2316
|
-
if (!
|
|
2317
|
-
|
|
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 (
|
|
2320
|
-
|
|
2487
|
+
if (fs7.existsSync(quarantinePath)) {
|
|
2488
|
+
fs7.rmSync(quarantinePath, { recursive: true, force: true });
|
|
2321
2489
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
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 || !
|
|
2507
|
+
if (!skillsDir || !fs7.existsSync(skillsDir)) {
|
|
2340
2508
|
return;
|
|
2341
2509
|
}
|
|
2342
2510
|
try {
|
|
2343
|
-
const entries =
|
|
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 =
|
|
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 (
|
|
2382
|
-
watcher =
|
|
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 =
|
|
2415
|
-
const destPath =
|
|
2416
|
-
if (!
|
|
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
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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 =
|
|
2442
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
2458
|
-
if (
|
|
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 (!
|
|
2638
|
+
if (!fs7.existsSync(QUARANTINE_DIR)) {
|
|
2471
2639
|
return results;
|
|
2472
2640
|
}
|
|
2473
|
-
const entries =
|
|
2641
|
+
const entries = fs7.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
|
|
2474
2642
|
for (const entry of entries) {
|
|
2475
2643
|
if (entry.isDirectory()) {
|
|
2476
|
-
const stat =
|
|
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:
|
|
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
|
-
|
|
2518
|
-
const entries =
|
|
2685
|
+
fs8.mkdirSync(dest, { recursive: true });
|
|
2686
|
+
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
2519
2687
|
for (const entry of entries) {
|
|
2520
|
-
const srcPath =
|
|
2521
|
-
const destPath =
|
|
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
|
-
|
|
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 =
|
|
2536
|
-
const srcDir =
|
|
2537
|
-
if (
|
|
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 (
|
|
2549
|
-
|
|
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 =
|
|
2565
|
-
const srcDir =
|
|
2566
|
-
if (
|
|
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 (!
|
|
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 (
|
|
2581
|
-
|
|
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
|
|
5105
|
-
import * as
|
|
5106
|
-
import { execSync as
|
|
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
|
|
5111
|
-
import * as
|
|
5112
|
-
import { execSync as
|
|
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 (
|
|
5174
|
-
return JSON.parse(
|
|
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 =
|
|
5183
|
-
if (!
|
|
5184
|
-
|
|
5350
|
+
const dir = path9.dirname(ANALYSIS_CACHE_PATH);
|
|
5351
|
+
if (!fs9.existsSync(dir)) {
|
|
5352
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5185
5353
|
}
|
|
5186
|
-
|
|
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 =
|
|
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
|
|
5329
|
-
import * as
|
|
5330
|
-
import { execSync as
|
|
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 (!
|
|
5333
|
-
|
|
5500
|
+
if (!fs10.existsSync(binDir)) {
|
|
5501
|
+
fs10.mkdirSync(binDir, { recursive: true });
|
|
5334
5502
|
}
|
|
5335
|
-
const wrapperPath =
|
|
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
|
-
|
|
5510
|
+
fs10.writeFileSync(wrapperPath, wrapperContent, { mode: 493 });
|
|
5343
5511
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5344
5512
|
try {
|
|
5345
|
-
|
|
5346
|
-
|
|
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 =
|
|
5519
|
+
const wrapperPath = path10.join(binDir, name);
|
|
5352
5520
|
try {
|
|
5353
|
-
if (
|
|
5354
|
-
|
|
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
|
|
5388
|
-
import * as
|
|
5389
|
-
import { execSync as
|
|
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
|
|
5560
|
+
return path11.join(agentHome, ".openclaw", "openclaw.json");
|
|
5393
5561
|
}
|
|
5394
5562
|
function readConfig() {
|
|
5395
5563
|
const configPath = getOpenClawConfigPath();
|
|
5396
5564
|
try {
|
|
5397
|
-
if (
|
|
5398
|
-
return JSON.parse(
|
|
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
|
-
|
|
5408
|
-
|
|
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 =
|
|
5578
|
+
const brokerUser = path11.basename(agentHome) + "_broker";
|
|
5411
5579
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5412
5580
|
try {
|
|
5413
|
-
|
|
5414
|
-
|
|
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
|
|
5444
|
-
import * as
|
|
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(
|
|
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:
|
|
5641
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5474
5642
|
});
|
|
5475
5643
|
if (!res.ok) {
|
|
5476
|
-
throw new Error(`Convex action ${
|
|
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 ${
|
|
5648
|
+
throw new Error(`Convex action ${path19}: ${body.errorMessage ?? "unknown error"}`);
|
|
5481
5649
|
}
|
|
5482
5650
|
return body.value;
|
|
5483
5651
|
}
|
|
5484
|
-
async function convexQuery(
|
|
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:
|
|
5657
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5490
5658
|
});
|
|
5491
5659
|
if (!res.ok) {
|
|
5492
|
-
throw new Error(`Convex query ${
|
|
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 ${
|
|
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
|
|
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 =
|
|
5596
|
-
const filesDir =
|
|
5597
|
-
|
|
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
|
-
|
|
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 =
|
|
5602
|
-
|
|
5603
|
-
|
|
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 =
|
|
5776
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5609
5777
|
try {
|
|
5610
|
-
if (!
|
|
5611
|
-
const meta = JSON.parse(
|
|
5778
|
+
if (!fs12.existsSync(metaPath)) return;
|
|
5779
|
+
const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
|
|
5612
5780
|
meta.analysis = analysis;
|
|
5613
|
-
|
|
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 (!
|
|
5787
|
+
if (!fs12.existsSync(baseDir)) return [];
|
|
5620
5788
|
const results = [];
|
|
5621
5789
|
try {
|
|
5622
|
-
const entries =
|
|
5790
|
+
const entries = fs12.readdirSync(baseDir, { withFileTypes: true });
|
|
5623
5791
|
for (const entry of entries) {
|
|
5624
5792
|
if (!entry.isDirectory()) continue;
|
|
5625
|
-
const metaPath =
|
|
5793
|
+
const metaPath = path12.join(baseDir, entry.name, "metadata.json");
|
|
5626
5794
|
try {
|
|
5627
|
-
const meta = JSON.parse(
|
|
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 =
|
|
5645
|
-
if (!
|
|
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 =
|
|
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(
|
|
5820
|
+
walk(path12.join(dir, entry.name), rel);
|
|
5653
5821
|
} else {
|
|
5654
|
-
const content =
|
|
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 =
|
|
5831
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5664
5832
|
try {
|
|
5665
|
-
if (
|
|
5666
|
-
return JSON.parse(
|
|
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 =
|
|
6204
|
-
if (
|
|
6371
|
+
const candidate = path13.join(dir, name);
|
|
6372
|
+
if (fs13.existsSync(candidate)) return candidate;
|
|
6205
6373
|
}
|
|
6206
|
-
const entries =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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:
|
|
6420
|
+
path: path13.join(skillsDir2 ?? "", a.name),
|
|
6253
6421
|
publisher: a.publisher,
|
|
6254
|
-
description: skillsDir2 ? readSkillDescription(
|
|
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:
|
|
6270
|
-
description: skillsDir2 ? readSkillDescription(
|
|
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 ?
|
|
6479
|
+
skillPath = skillsDir2 ? path13.join(skillsDir2, name) : "";
|
|
6312
6480
|
} else if (skillsDir2) {
|
|
6313
6481
|
source = "workspace";
|
|
6314
6482
|
status = "workspace";
|
|
6315
|
-
skillPath =
|
|
6483
|
+
skillPath = path13.join(skillsDir2, name);
|
|
6316
6484
|
}
|
|
6317
6485
|
let content = "";
|
|
6318
6486
|
let metadata;
|
|
6319
|
-
const dirToRead = skillPath || (skillsDir2 ?
|
|
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 =
|
|
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 =
|
|
6621
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6454
6622
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6455
|
-
const skillDir =
|
|
6456
|
-
const isInstalled =
|
|
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
|
-
|
|
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
|
-
|
|
6668
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6501
6669
|
for (const file of files) {
|
|
6502
|
-
const filePath =
|
|
6503
|
-
|
|
6504
|
-
|
|
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
|
-
|
|
6508
|
-
|
|
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 (
|
|
6520
|
-
|
|
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 =
|
|
6726
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6559
6727
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6560
|
-
const skillDir =
|
|
6728
|
+
const skillDir = path13.join(skillsDir2, name);
|
|
6561
6729
|
try {
|
|
6562
6730
|
addToApprovedList(name, publisher);
|
|
6563
|
-
|
|
6731
|
+
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6564
6732
|
for (const file of files) {
|
|
6565
|
-
const filePath =
|
|
6566
|
-
|
|
6567
|
-
|
|
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
|
-
|
|
6571
|
-
|
|
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 (
|
|
6580
|
-
|
|
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
|
|
6596
|
-
import * as
|
|
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 (!
|
|
6777
|
+
if (!fs14.existsSync(ALLOWED_COMMANDS_PATH2)) {
|
|
6610
6778
|
return { version: "1.0.0", commands: [] };
|
|
6611
6779
|
}
|
|
6612
6780
|
try {
|
|
6613
|
-
const content =
|
|
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 =
|
|
6621
|
-
if (!
|
|
6622
|
-
|
|
6788
|
+
const dir = path14.dirname(ALLOWED_COMMANDS_PATH2);
|
|
6789
|
+
if (!fs14.existsSync(dir)) {
|
|
6790
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
6623
6791
|
}
|
|
6624
|
-
|
|
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 (!
|
|
6634
|
-
const entries =
|
|
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 =
|
|
6805
|
+
const fullPath = path14.join(dir, entry);
|
|
6638
6806
|
try {
|
|
6639
|
-
const stat =
|
|
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 (!
|
|
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
|
|
7120
|
-
import * as
|
|
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 =
|
|
7484
|
+
const binDir = path15.join(agentHome, "bin");
|
|
7317
7485
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
7318
|
-
skillDir =
|
|
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 &&
|
|
7362
|
-
|
|
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
|
|
7386
|
-
import * as
|
|
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 =
|
|
7561
|
+
const resolvedPath = path16.resolve(dirPath);
|
|
7394
7562
|
let dirents;
|
|
7395
7563
|
try {
|
|
7396
|
-
dirents =
|
|
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:
|
|
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
|
|
7420
|
-
import * as
|
|
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 =
|
|
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 (!
|
|
7442
|
-
const content =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
7493
|
-
const content =
|
|
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
|
-
|
|
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
|
|
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
|
|
7553
|
-
if (
|
|
7554
|
-
|
|
7726
|
+
let path19 = parsed.pathname;
|
|
7727
|
+
if (path19.length > 1) {
|
|
7728
|
+
path19 = path19.replace(/\/+$/, "");
|
|
7555
7729
|
}
|
|
7556
|
-
return `${parsed.protocol}//${parsed.host}${
|
|
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
|
-
|
|
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
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
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
|
|
7791
|
-
import * as
|
|
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 =
|
|
7970
|
+
var __dirname = path18.dirname(__filename);
|
|
7795
7971
|
function getUiAssetsPath() {
|
|
7796
|
-
const pkgRootPath =
|
|
7797
|
-
if (
|
|
7972
|
+
const pkgRootPath = path18.join(__dirname, "..", "ui-assets");
|
|
7973
|
+
if (fs18.existsSync(pkgRootPath)) {
|
|
7798
7974
|
return pkgRootPath;
|
|
7799
7975
|
}
|
|
7800
|
-
const bundledPath =
|
|
7801
|
-
if (
|
|
7976
|
+
const bundledPath = path18.join(__dirname, "ui-assets");
|
|
7977
|
+
if (fs18.existsSync(bundledPath)) {
|
|
7802
7978
|
return bundledPath;
|
|
7803
7979
|
}
|
|
7804
|
-
const devPath =
|
|
7805
|
-
if (
|
|
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 (!
|
|
7895
|
-
|
|
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
|
-
|
|
8118
|
+
fs20.writeFileSync(pidPath, process.pid.toString(), "utf-8");
|
|
7932
8119
|
const cleanup = () => {
|
|
7933
|
-
if (
|
|
7934
|
-
|
|
8120
|
+
if (fs20.existsSync(pidPath)) {
|
|
8121
|
+
fs20.unlinkSync(pidPath);
|
|
7935
8122
|
}
|
|
7936
8123
|
process.exit(0);
|
|
7937
8124
|
};
|