@kafca/agentdock 0.1.14 → 0.1.15

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.
Files changed (27) hide show
  1. package/dist/renderer/assets/{index-BM0Jl5gB.js → index-BSFT2vom.js} +136 -121
  2. package/dist/renderer/assets/index-tUukeThX.css +11 -0
  3. package/dist/renderer/index.html +2 -2
  4. package/dist-electron/electron/agent-runtime-detector.test.js +121 -0
  5. package/dist-electron/electron/lac-cli.test.js +63 -0
  6. package/dist-electron/electron/local-core-refactor.test.js +54 -0
  7. package/dist-electron/electron/plugin-kernel.test.js +6 -6
  8. package/dist-electron/electron/runtime-detection-service.test.js +116 -0
  9. package/dist-electron/electron/security-approval-store.test.js +89 -0
  10. package/dist-electron/electron/workspace-task-store.test.js +66 -0
  11. package/dist-electron/packages/core-sdk/src/index.js +124 -0
  12. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +68 -0
  13. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +617 -0
  14. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +11 -0
  15. package/dist-electron/services/local-ai-core/src/cli/lac.js +19 -12
  16. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +5 -0
  17. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-local-plugin.js +35 -0
  18. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +197 -0
  19. package/dist-electron/services/local-ai-core/src/runtime/agent-runtime-detector.js +237 -0
  20. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +72 -0
  21. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-service.js +138 -0
  22. package/dist-electron/services/local-ai-core/src/runtime/runtime-detection-store.js +46 -0
  23. package/dist-electron/services/local-ai-core/src/runtime/server.js +151 -0
  24. package/dist-electron/services/local-ai-core/src/scheduler/local-schedule-adapter.js +61 -0
  25. package/dist-electron/services/local-ai-core/src/security/command-risk.js +57 -0
  26. package/package.json +1 -1
  27. package/dist/renderer/assets/index-DrLbJPYN.css +0 -11
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RuntimeDetectionService = void 0;
4
+ const desktop_js_1 = require("../../../../shared/desktop.js");
5
+ const agent_runtime_detector_js_1 = require("./agent-runtime-detector.js");
6
+ const runtime_detection_store_js_1 = require("./runtime-detection-store.js");
7
+ class RuntimeDetectionService {
8
+ options;
9
+ store;
10
+ detect;
11
+ refreshPromise = null;
12
+ checkingRuntimeIds = new Set();
13
+ constructor(options) {
14
+ this.options = options;
15
+ this.store = new runtime_detection_store_js_1.RuntimeDetectionStore(options.userDataPath);
16
+ this.detect = options.detect || agent_runtime_detector_js_1.detectInstalledAgentRuntimes;
17
+ }
18
+ list(runtimeId) {
19
+ const cached = this.store.read() || this.createUnknownResults();
20
+ return this.filter(cached, runtimeId);
21
+ }
22
+ isChecking(runtimeId) {
23
+ if (!runtimeId) {
24
+ return this.checkingRuntimeIds.size > 0;
25
+ }
26
+ return this.checkingRuntimeIds.has(runtimeId);
27
+ }
28
+ async refresh(runtimeId) {
29
+ if (this.refreshPromise) {
30
+ const runtimes = await this.refreshPromise;
31
+ return this.filter(runtimes, runtimeId);
32
+ }
33
+ this.refreshPromise = this.runRefresh(runtimeId).finally(() => {
34
+ this.refreshPromise = null;
35
+ });
36
+ const runtimes = await this.refreshPromise;
37
+ return this.filter(runtimes, runtimeId);
38
+ }
39
+ async refreshOnStartup() {
40
+ try {
41
+ await this.refresh();
42
+ }
43
+ catch (err) {
44
+ this.options.log?.(`Startup runtime detection failed: ${err?.message || String(err)}`);
45
+ }
46
+ }
47
+ async runRefresh(runtimeId) {
48
+ const previous = this.store.read() || [];
49
+ const startedAt = new Date().toISOString();
50
+ this.markChecking(runtimeId);
51
+ this.emit({ type: 'runtime.detect.started', runtimeId, detectedAt: startedAt });
52
+ this.options.log?.(runtimeId
53
+ ? `Started runtime detection for ${runtimeId}.`
54
+ : 'Started runtime detection for all runtimes.');
55
+ try {
56
+ const config = await this.options.readConfig();
57
+ const detected = this.detect({ config });
58
+ this.store.write(detected);
59
+ const completedAt = new Date().toISOString();
60
+ this.emit({ type: 'runtime.detect.completed', runtimeId, detectedAt: completedAt, runtimes: this.filter(detected, runtimeId) });
61
+ for (const runtime of changedRuntimes(previous, detected)) {
62
+ this.emit({ type: 'runtime.status.changed', runtime });
63
+ }
64
+ this.options.log?.(runtimeId
65
+ ? `Completed runtime detection for ${runtimeId}.`
66
+ : 'Completed runtime detection for all runtimes.');
67
+ return detected;
68
+ }
69
+ catch (err) {
70
+ const message = err?.message || String(err);
71
+ this.emit({ type: 'runtime.detect.failed', runtimeId, detectedAt: new Date().toISOString(), error: message });
72
+ this.options.log?.(`Runtime detection failed: ${message}`);
73
+ throw err;
74
+ }
75
+ finally {
76
+ this.unmarkChecking();
77
+ }
78
+ }
79
+ createUnknownResults() {
80
+ const detectedAt = new Date(0).toISOString();
81
+ return desktop_js_1.DESKTOP_AGENT_TYPE_OPTIONS.map((agentType) => ({
82
+ agentType,
83
+ runtimeId: agentType,
84
+ displayName: displayName(agentType),
85
+ status: agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE ? 'installed' : 'unknown',
86
+ installed: agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE,
87
+ detectedAt,
88
+ summary: agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE
89
+ ? `${displayName(agentType)} is built in.`
90
+ : `${displayName(agentType)} has not been checked yet.`,
91
+ issues: [],
92
+ recommendedActions: agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE
93
+ ? []
94
+ : [{ label: 'Run detection', description: 'Refresh runtime detection to check this machine.' }],
95
+ source: agentType === desktop_js_1.LOCALCORE_ACP_AGENT_TYPE ? 'builtin' : 'path',
96
+ }));
97
+ }
98
+ filter(runtimes, runtimeId) {
99
+ if (!runtimeId) {
100
+ return runtimes;
101
+ }
102
+ return runtimes.filter((runtime) => runtime.runtimeId === runtimeId || runtime.agentType === runtimeId);
103
+ }
104
+ markChecking(runtimeId) {
105
+ this.checkingRuntimeIds = new Set(runtimeId ? [runtimeId] : desktop_js_1.DESKTOP_AGENT_TYPE_OPTIONS);
106
+ }
107
+ unmarkChecking() {
108
+ this.checkingRuntimeIds.clear();
109
+ }
110
+ emit(event) {
111
+ this.options.emit?.(event);
112
+ }
113
+ }
114
+ exports.RuntimeDetectionService = RuntimeDetectionService;
115
+ function changedRuntimes(previous, next) {
116
+ const previousById = new Map(previous.map((runtime) => [runtime.runtimeId, runtime]));
117
+ return next.filter((runtime) => {
118
+ const before = previousById.get(runtime.runtimeId);
119
+ return !before
120
+ || before.status !== runtime.status
121
+ || before.version !== runtime.version
122
+ || before.binaryPath !== runtime.binaryPath
123
+ || before.error !== runtime.error;
124
+ });
125
+ }
126
+ function displayName(agentType) {
127
+ const names = {
128
+ opencode: 'OpenCode',
129
+ codex: 'Codex',
130
+ claudecode: 'Claude Code',
131
+ cursor: 'Cursor',
132
+ gemini: 'Gemini',
133
+ qoder: 'Qoder',
134
+ iflow: 'iFlow',
135
+ [desktop_js_1.LOCALCORE_ACP_AGENT_TYPE]: 'LocalCore ACP',
136
+ };
137
+ return names[agentType] || agentType;
138
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RuntimeDetectionStore = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ class RuntimeDetectionStore {
7
+ path;
8
+ constructor(userDataPath) {
9
+ this.path = (0, node_path_1.join)(userDataPath, 'runtime', 'runtime-detection.json');
10
+ }
11
+ read() {
12
+ if (!(0, node_fs_1.existsSync)(this.path)) {
13
+ return null;
14
+ }
15
+ try {
16
+ const payload = JSON.parse((0, node_fs_1.readFileSync)(this.path, 'utf8'));
17
+ if (!Array.isArray(payload.runtimes)) {
18
+ return null;
19
+ }
20
+ return payload.runtimes.filter(isRuntimeDetectionResult);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ write(runtimes) {
27
+ const payload = {
28
+ version: 1,
29
+ updatedAt: new Date().toISOString(),
30
+ runtimes,
31
+ };
32
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.path), { recursive: true });
33
+ (0, node_fs_1.writeFileSync)(this.path, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
34
+ }
35
+ }
36
+ exports.RuntimeDetectionStore = RuntimeDetectionStore;
37
+ function isRuntimeDetectionResult(value) {
38
+ const runtime = value;
39
+ return typeof runtime?.agentType === 'string'
40
+ && typeof runtime.runtimeId === 'string'
41
+ && typeof runtime.displayName === 'string'
42
+ && typeof runtime.status === 'string'
43
+ && typeof runtime.detectedAt === 'string'
44
+ && Array.isArray(runtime.issues)
45
+ && Array.isArray(runtime.recommendedActions);
46
+ }
@@ -67,6 +67,9 @@ class LocalAiCoreServer {
67
67
  this.bindings.on('scheduler-run', (run) => {
68
68
  this.broadcast({ type: 'scheduler.run.updated', run });
69
69
  });
70
+ this.bindings.on('runtime-detection', (event) => {
71
+ this.broadcast(event);
72
+ });
70
73
  }
71
74
  async start() {
72
75
  await new Promise((resolve, reject) => {
@@ -131,6 +134,36 @@ class LocalAiCoreServer {
131
134
  json(res, 200, this.bindings.getLogs(limit));
132
135
  return;
133
136
  }
137
+ if (req.method === 'GET' && path === '/api/local/v1/runtime/agent-runtimes') {
138
+ json(res, 200, await this.runtimeDetectionResponse());
139
+ return;
140
+ }
141
+ if (req.method === 'GET' && path === '/api/local/v1/runtimes') {
142
+ json(res, 200, await this.runtimeDetectionResponse());
143
+ return;
144
+ }
145
+ if (req.method === 'GET' && path.startsWith('/api/local/v1/runtimes/')) {
146
+ const runtimeId = decodeURIComponent(path.slice('/api/local/v1/runtimes/'.length));
147
+ const runtimes = await this.bindings.listInstalledAgentRuntimes();
148
+ const runtime = runtimes.find((entry) => entry.runtimeId === runtimeId || entry.agentType === runtimeId);
149
+ if (!runtime) {
150
+ json(res, 404, null, false, 'Runtime not found');
151
+ return;
152
+ }
153
+ json(res, 200, runtime);
154
+ return;
155
+ }
156
+ if (req.method === 'POST' && path === '/api/local/v1/runtimes/refresh') {
157
+ const runtimes = await this.bindings.refreshInstalledAgentRuntimes();
158
+ json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning() });
159
+ return;
160
+ }
161
+ if (req.method === 'POST' && path.startsWith('/api/local/v1/runtimes/') && path.endsWith('/refresh')) {
162
+ const runtimeId = decodeURIComponent(path.slice('/api/local/v1/runtimes/'.length, -'/refresh'.length));
163
+ const runtimes = await this.bindings.refreshInstalledAgentRuntimes(runtimeId);
164
+ json(res, 200, { runtimes, checking: this.bindings.isRuntimeDetectionRunning(runtimeId) });
165
+ return;
166
+ }
134
167
  if (req.method === 'GET' && path === '/api/local/v1/runtime/config') {
135
168
  json(res, 200, await this.bindings.readConfigFile());
136
169
  return;
@@ -264,6 +297,118 @@ class LocalAiCoreServer {
264
297
  json(res, 200, { workspaces: await this.bindings.listWorkspaces() });
265
298
  return;
266
299
  }
300
+ if (req.method === 'GET' && path === '/api/local/v1/workspace-registry') {
301
+ json(res, 200, { workspaces: await this.bindings.listWorkspaceRegistry() });
302
+ return;
303
+ }
304
+ if (req.method === 'POST' && path === '/api/local/v1/workspace-registry') {
305
+ const body = await readJsonBody(req);
306
+ json(res, 200, await this.bindings.createWorkspaceRegistryEntry(body));
307
+ return;
308
+ }
309
+ if (path.startsWith('/api/local/v1/workspace-registry/')) {
310
+ const workspaceId = decodeURIComponent(path.slice('/api/local/v1/workspace-registry/'.length));
311
+ if (req.method === 'GET') {
312
+ json(res, 200, await this.bindings.getWorkspaceRegistryEntry(workspaceId));
313
+ return;
314
+ }
315
+ if (req.method === 'PATCH') {
316
+ const body = await readJsonBody(req);
317
+ json(res, 200, await this.bindings.updateWorkspaceRegistryEntry(workspaceId, body));
318
+ return;
319
+ }
320
+ if (req.method === 'DELETE') {
321
+ json(res, 200, await this.bindings.deleteWorkspaceRegistryEntry(workspaceId));
322
+ return;
323
+ }
324
+ }
325
+ if (path.startsWith('/api/local/v1/workspace-security/')) {
326
+ const workspaceId = decodeURIComponent(path.slice('/api/local/v1/workspace-security/'.length));
327
+ if (req.method === 'GET') {
328
+ json(res, 200, await this.bindings.getWorkspaceSecuritySettings(workspaceId));
329
+ return;
330
+ }
331
+ if (req.method === 'PATCH') {
332
+ const body = await readJsonBody(req);
333
+ json(res, 200, await this.bindings.updateWorkspaceSecuritySettings(workspaceId, body));
334
+ return;
335
+ }
336
+ }
337
+ if (req.method === 'POST' && path === '/api/local/v1/security/command-risk') {
338
+ const body = await readJsonBody(req);
339
+ json(res, 200, await this.bindings.classifyCommand(String(body.command || ''), String(body.workspaceId || '') || undefined));
340
+ return;
341
+ }
342
+ if (req.method === 'GET' && path === '/api/local/v1/approvals') {
343
+ const statusParam = url.searchParams.get('status') || '';
344
+ const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
345
+ json(res, 200, await this.bindings.listApprovalRequests({
346
+ workspaceId: url.searchParams.get('workspace_id') || undefined,
347
+ taskId: url.searchParams.get('task_id') || undefined,
348
+ status,
349
+ limit: Number(url.searchParams.get('limit') || '50'),
350
+ }));
351
+ return;
352
+ }
353
+ if (req.method === 'POST' && path === '/api/local/v1/approvals') {
354
+ const body = await readJsonBody(req);
355
+ json(res, 200, await this.bindings.createApprovalRequest(body));
356
+ return;
357
+ }
358
+ if (path.startsWith('/api/local/v1/approvals/')) {
359
+ const suffix = path.slice('/api/local/v1/approvals/'.length);
360
+ if (req.method === 'POST' && suffix.endsWith('/resolve')) {
361
+ const approvalId = decodeURIComponent(suffix.slice(0, -'/resolve'.length));
362
+ const body = await readJsonBody(req);
363
+ json(res, 200, await this.bindings.resolveApprovalRequest(approvalId, body));
364
+ return;
365
+ }
366
+ if (req.method === 'GET') {
367
+ const approvalId = decodeURIComponent(suffix);
368
+ json(res, 200, await this.bindings.getApprovalRequest(approvalId));
369
+ return;
370
+ }
371
+ }
372
+ if (req.method === 'GET' && path === '/api/local/v1/audit-events') {
373
+ const typeParam = url.searchParams.get('type') || '';
374
+ const type = typeParam ? typeParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
375
+ json(res, 200, await this.bindings.listAuditEvents({
376
+ workspaceId: url.searchParams.get('workspace_id') || undefined,
377
+ taskId: url.searchParams.get('task_id') || undefined,
378
+ approvalId: url.searchParams.get('approval_id') || undefined,
379
+ type,
380
+ limit: Number(url.searchParams.get('limit') || '50'),
381
+ }));
382
+ return;
383
+ }
384
+ if (req.method === 'GET' && path === '/api/local/v1/tasks') {
385
+ const statusParam = url.searchParams.get('status') || '';
386
+ const status = statusParam ? statusParam.split(',').map((item) => item.trim()).filter(Boolean) : undefined;
387
+ json(res, 200, await this.bindings.listAgentTasks({
388
+ workspaceId: url.searchParams.get('workspace_id') || undefined,
389
+ runtimeId: url.searchParams.get('runtime_id') || undefined,
390
+ status,
391
+ limit: Number(url.searchParams.get('limit') || '50'),
392
+ }));
393
+ return;
394
+ }
395
+ if (req.method === 'POST' && path === '/api/local/v1/tasks') {
396
+ const body = await readJsonBody(req);
397
+ json(res, 200, await this.bindings.createAgentTask(body));
398
+ return;
399
+ }
400
+ if (path.startsWith('/api/local/v1/tasks/')) {
401
+ const taskId = decodeURIComponent(path.slice('/api/local/v1/tasks/'.length));
402
+ if (req.method === 'GET') {
403
+ json(res, 200, await this.bindings.getAgentTask(taskId));
404
+ return;
405
+ }
406
+ if (req.method === 'PATCH') {
407
+ const body = await readJsonBody(req);
408
+ json(res, 200, await this.bindings.updateAgentTask(taskId, body));
409
+ return;
410
+ }
411
+ }
267
412
  if (req.method === 'GET' && path === '/api/local/v1/threads') {
268
413
  const workspaceId = String(url.searchParams.get('workspace_id') || '');
269
414
  json(res, 200, { threads: workspaceId ? await this.bindings.listThreads(workspaceId) : [] });
@@ -454,6 +599,12 @@ class LocalAiCoreServer {
454
599
  client.write(payload);
455
600
  }
456
601
  }
602
+ async runtimeDetectionResponse() {
603
+ return {
604
+ runtimes: await this.bindings.listInstalledAgentRuntimes(),
605
+ checking: this.bindings.isRuntimeDetectionRunning(),
606
+ };
607
+ }
457
608
  findThreadIdFromSessionKey(sessionKey) {
458
609
  const parts = sessionKey.split(':');
459
610
  if (parts.length < 3) {
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalScheduleAdapter = void 0;
4
+ const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ class LocalScheduleAdapter {
6
+ options;
7
+ deliveryTargets = ['local'];
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ supports(job) {
12
+ return job.platform === 'local' && (job.route.type === 'local.thread' || job.route.type === 'thread');
13
+ }
14
+ async execute(context) {
15
+ const { job } = context;
16
+ const workspaceRouter = this.options.getWorkspaceRouter();
17
+ const threadId = await this.resolveThread(job);
18
+ const sendResult = await workspaceRouter.sendThreadMessage(threadId, job.promptTemplate);
19
+ await this.waitForRun(sendResult.runId);
20
+ const thread = await workspaceRouter.getThread(threadId);
21
+ const replyText = [...thread.messages]
22
+ .reverse()
23
+ .find((message) => message.role === 'assistant' && message.kind === 'final')
24
+ ?.content;
25
+ return {
26
+ threadId,
27
+ runId: sendResult.runId,
28
+ replyText,
29
+ };
30
+ }
31
+ async resolveThread(job) {
32
+ const workspaceRouter = this.options.getWorkspaceRouter();
33
+ if (job.route.threadId) {
34
+ await workspaceRouter.getThread(job.route.threadId);
35
+ return job.route.threadId;
36
+ }
37
+ const title = `[Scheduled] ${job.description || job.id}`;
38
+ const existing = (await workspaceRouter.listThreads(job.workspaceId))
39
+ .find((thread) => thread.title === title);
40
+ if (existing) {
41
+ return existing.id;
42
+ }
43
+ const created = await workspaceRouter.createThread(job.workspaceId, title);
44
+ return created.id;
45
+ }
46
+ async waitForRun(runId, timeoutMs = 15 * 60 * 1000) {
47
+ const startedAt = Date.now();
48
+ while (Date.now() - startedAt < timeoutMs) {
49
+ const run = this.options.store.getRun(runId);
50
+ if (run && TERMINAL_RUN_STATES.has(run.status)) {
51
+ if (run.status !== 'completed') {
52
+ throw new Error(`Scheduled run finished with status ${run.status}`);
53
+ }
54
+ return;
55
+ }
56
+ await new Promise((resolve) => setTimeout(resolve, 300));
57
+ }
58
+ throw new Error(`Timed out waiting for scheduled run ${runId}`);
59
+ }
60
+ }
61
+ exports.LocalScheduleAdapter = LocalScheduleAdapter;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyCommandRisk = classifyCommandRisk;
4
+ function classifyCommandRisk(command) {
5
+ const normalized = command.trim();
6
+ const lower = normalized.toLowerCase();
7
+ const scopes = new Set(['command.execute']);
8
+ const reasons = [];
9
+ let riskLevel = 'low';
10
+ const highRiskPatterns = [
11
+ /\brm\s+(-[^\s]*r|-[^\s]*f|-rf|-fr)\b/,
12
+ /\bsudo\b/,
13
+ /\bchmod\s+(-?R\s+)?(777|[+]s)\b/,
14
+ /\bchown\s+(-?R\s+)?/,
15
+ /\bgit\s+(reset|clean|push|rebase)\b/,
16
+ /\bmkfs\b|\bdd\s+if=/,
17
+ /\b(security|pass|op|vault)\b.*\b(read|show|get)\b/,
18
+ ];
19
+ const mediumRiskPatterns = [
20
+ /\bnpm\s+(install|i|update)\b/,
21
+ /\bpnpm\s+(add|install|update)\b/,
22
+ /\byarn\s+(add|install|upgrade)\b/,
23
+ /\bcurl\b|\bwget\b/,
24
+ /\bgit\s+(checkout|merge|commit|pull)\b/,
25
+ /\bpython\d?\s+.*-m\s+pip\s+install\b/,
26
+ ];
27
+ if (highRiskPatterns.some((pattern) => pattern.test(lower))) {
28
+ riskLevel = 'high';
29
+ reasons.push('Command matches high-risk mutation, privilege, secret, or Git state rules.');
30
+ }
31
+ else if (mediumRiskPatterns.some((pattern) => pattern.test(lower))) {
32
+ riskLevel = 'medium';
33
+ reasons.push('Command may modify dependencies, network state, or Git working state.');
34
+ }
35
+ else {
36
+ reasons.push('Command does not match the current medium or high-risk rules.');
37
+ }
38
+ if (/\bcurl\b|\bwget\b|\bnpm\b|\bpnpm\b|\byarn\b|\bpip\b/.test(lower)) {
39
+ scopes.add('network.access');
40
+ }
41
+ if (/\bgit\s+(reset|clean|push|rebase|checkout|merge|commit|pull)\b/.test(lower)) {
42
+ scopes.add('git.modify');
43
+ }
44
+ if (/\brm\b|\bmv\b|\bcp\b|\bchmod\b|\bchown\b|\btouch\b|>\s*[^&]|\btee\b/.test(lower)) {
45
+ scopes.add('workspace.write');
46
+ }
47
+ if (/\b(secret|token|api[_-]?key|password|vault|op\s+read|pass\s+show)\b/.test(lower)) {
48
+ scopes.add('secrets.access');
49
+ }
50
+ return {
51
+ command: normalized,
52
+ riskLevel,
53
+ scopes: [...scopes],
54
+ reasons,
55
+ requiresApproval: riskLevel !== 'low',
56
+ };
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "main": "dist-electron/electron/main.js",
6
6
  "bin": {