@kernel.chat/kbot 4.1.1 → 4.4.0
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/dist/adapters/agent-sdk/from-agent-sdk.d.ts +28 -0
- package/dist/adapters/agent-sdk/from-agent-sdk.js +107 -0
- package/dist/adapters/agent-sdk/index.d.ts +4 -0
- package/dist/adapters/agent-sdk/index.js +14 -0
- package/dist/adapters/agent-sdk/to-agent-sdk.d.ts +13 -0
- package/dist/adapters/agent-sdk/to-agent-sdk.js +73 -0
- package/dist/adapters/agent-sdk/types.d.ts +41 -0
- package/dist/adapters/agent-sdk/types.js +9 -0
- package/dist/adapters/peekaboo/commands.d.ts +42 -0
- package/dist/adapters/peekaboo/commands.js +209 -0
- package/dist/adapters/peekaboo/index.d.ts +4 -0
- package/dist/adapters/peekaboo/index.js +9 -0
- package/dist/adapters/peekaboo/runner.d.ts +25 -0
- package/dist/adapters/peekaboo/runner.js +75 -0
- package/dist/adapters/peekaboo/types.d.ts +69 -0
- package/dist/adapters/peekaboo/types.js +8 -0
- package/dist/cli.js +15 -1
- package/dist/permissions.d.ts +17 -0
- package/dist/permissions.js +48 -0
- package/dist/tool-pipeline.js +11 -0
- package/dist/tools/computer.js +64 -0
- package/dist/tools/peekaboo.d.ts +4 -0
- package/dist/tools/peekaboo.js +371 -0
- package/dist/tools/security-audit-local.d.ts +47 -0
- package/dist/tools/security-audit-local.js +363 -0
- package/dist/tools/stream-overlay.d.ts +9 -0
- package/dist/tools/stream-overlay.js +167 -134
- package/dist/tools/stream-renderer.js +10 -1
- package/dist/tools/swarm-2026-04.js +4 -0
- package/package.json +1 -1
- package/skills/ai-engineering/full-stack-mastery/SKILL.md +174 -0
- package/skills/native-automation/peekaboo-snapshot-act/SKILL.md +91 -0
- package/skills/security-audit/dependency-audit/SKILL.md +102 -0
- package/skills/security-audit/local-vulnerability-hunt/SKILL.md +127 -0
- package/skills/security-audit/secrets-leak-scan/SKILL.md +109 -0
- package/skills/security-audit/threat-model-quickdraw/SKILL.md +107 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// Peekaboo tools — kbot tool-registry surface for the macOS Peekaboo CLI.
|
|
2
|
+
//
|
|
3
|
+
// Wraps src/adapters/peekaboo so the LLM tool layer can drive AX-aware
|
|
4
|
+
// snapshots, clicks, typing, value-set, named-action invocation, and the
|
|
5
|
+
// peekaboo agent subcommand. App-bound calls are gated by the same per-app
|
|
6
|
+
// approval contract computer.ts uses; because computer.ts does not export
|
|
7
|
+
// its in-process `approvedApps` set, this module falls back to checking
|
|
8
|
+
// the on-disk Coordinator lock file at ~/.kbot/computer-use/<app>.lock as
|
|
9
|
+
// a best-effort cross-process signal — see requireApproval() below.
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { registerTool } from './index.js';
|
|
14
|
+
import { see, click, type_, setValue, performAction, agent, peekabooAvailable, } from '../adapters/peekaboo/index.js';
|
|
15
|
+
// ── Approval gate ──────────────────────────────────────────────────────
|
|
16
|
+
//
|
|
17
|
+
// computer.ts holds an in-process `approvedApps` Set keyed by lowercase app
|
|
18
|
+
// name, plus a Coordinator that writes a per-app lock file at
|
|
19
|
+
// ~/.kbot/computer-use/<app>.lock when an *active* claim is held.
|
|
20
|
+
//
|
|
21
|
+
// Limitation: the `approvedApps` set is not exported and the Coordinator
|
|
22
|
+
// lock file only exists during an active claim, not after a bare
|
|
23
|
+
// app_approve. So from a sibling module like this one we cannot directly
|
|
24
|
+
// observe approval state. We treat the lock-file presence as the strongest
|
|
25
|
+
// available cross-process approval signal — if the user has driven the app
|
|
26
|
+
// through computer.ts at all this session, the lock file will exist
|
|
27
|
+
// (computer.ts re-acquires + retains during the call). When we can't see
|
|
28
|
+
// it, we fail closed with a clear pointer to `app_approve`.
|
|
29
|
+
const LOCK_ROOT = process.env.KBOT_COMPUTER_USE_ROOT || join(homedir(), '.kbot', 'computer-use');
|
|
30
|
+
/** Mirror of computer-use-coordinator.ts#sanitizeApp — keep in sync. */
|
|
31
|
+
function sanitizeApp(app) {
|
|
32
|
+
return app.replace(/[/\\ -]/g, '_');
|
|
33
|
+
}
|
|
34
|
+
function lockPath(app) {
|
|
35
|
+
return join(LOCK_ROOT, `${sanitizeApp(app.toLowerCase())}.lock`);
|
|
36
|
+
}
|
|
37
|
+
/** Returns null when the gate passes, or an `Error: ...` string on denial. */
|
|
38
|
+
function requireApproval(app) {
|
|
39
|
+
if (!app)
|
|
40
|
+
return null;
|
|
41
|
+
if (existsSync(lockPath(app)))
|
|
42
|
+
return null;
|
|
43
|
+
return `Error: ${app} is not approved for computer use. Run app_approve first (or drive a computer.ts tool against ${app} so the Coordinator lock is created).`;
|
|
44
|
+
}
|
|
45
|
+
// ── Outcome → string helpers ───────────────────────────────────────────
|
|
46
|
+
function outcomeToString(out) {
|
|
47
|
+
if (!out.ok) {
|
|
48
|
+
const err = out.error;
|
|
49
|
+
const detail = err.stderr?.trim() || err.message;
|
|
50
|
+
return `Error: peekaboo ${err.code}: ${detail}`;
|
|
51
|
+
}
|
|
52
|
+
// Strip the discriminant before pretty-printing the success payload.
|
|
53
|
+
const { ok: _ok, ...payload } = out;
|
|
54
|
+
void _ok;
|
|
55
|
+
return JSON.stringify(payload, null, 2);
|
|
56
|
+
}
|
|
57
|
+
async function ensureBinary() {
|
|
58
|
+
const ok = await peekabooAvailable();
|
|
59
|
+
if (ok)
|
|
60
|
+
return null;
|
|
61
|
+
return "Error: peekaboo CLI not found on PATH. Install via 'brew install steipete/tap/peekaboo' or 'npm i -g @steipete/peekaboo'.";
|
|
62
|
+
}
|
|
63
|
+
// ── Tool definitions ───────────────────────────────────────────────────
|
|
64
|
+
const peekabooSee = {
|
|
65
|
+
name: 'peekaboo_see',
|
|
66
|
+
description: 'Capture an AX snapshot of an app or the screen via the Peekaboo CLI. Returns a snapshot id plus a list of labeled element ids (B1, T1, …) usable by peekaboo_click / peekaboo_set_value / peekaboo_perform_action.',
|
|
67
|
+
parameters: {
|
|
68
|
+
app: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'Target app name (optional). When supplied, the app must be approved via app_approve.',
|
|
71
|
+
},
|
|
72
|
+
mode: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'Capture mode: "screen" (default) or "window".',
|
|
75
|
+
},
|
|
76
|
+
retina: {
|
|
77
|
+
type: 'boolean',
|
|
78
|
+
description: 'Capture at retina resolution (optional).',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
tier: 'free',
|
|
82
|
+
async execute(args) {
|
|
83
|
+
const binErr = await ensureBinary();
|
|
84
|
+
if (binErr)
|
|
85
|
+
return binErr;
|
|
86
|
+
const app = typeof args.app === 'string' && args.app.length > 0 ? args.app : undefined;
|
|
87
|
+
if (app) {
|
|
88
|
+
const gate = requireApproval(app);
|
|
89
|
+
if (gate)
|
|
90
|
+
return gate;
|
|
91
|
+
}
|
|
92
|
+
const mode = args.mode === 'window' || args.mode === 'screen' ? args.mode : undefined;
|
|
93
|
+
const retina = args.retina === true;
|
|
94
|
+
try {
|
|
95
|
+
const out = await see({
|
|
96
|
+
...(app ? { app } : {}),
|
|
97
|
+
...(mode ? { mode } : {}),
|
|
98
|
+
...(retina ? { retina: true } : {}),
|
|
99
|
+
});
|
|
100
|
+
return outcomeToString(out);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
return `Error: ${e.message}`;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const peekabooClick = {
|
|
108
|
+
name: 'peekaboo_click',
|
|
109
|
+
description: 'Click against a Peekaboo snapshot. Provide either `on` (element id or query) or `coords` ("x,y"). Requires a prior peekaboo_see.',
|
|
110
|
+
parameters: {
|
|
111
|
+
app: {
|
|
112
|
+
type: 'string',
|
|
113
|
+
description: 'App being targeted. Required for the approval gate.',
|
|
114
|
+
required: true,
|
|
115
|
+
},
|
|
116
|
+
snapshot: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Snapshot id from peekaboo_see.',
|
|
119
|
+
required: true,
|
|
120
|
+
},
|
|
121
|
+
on: {
|
|
122
|
+
type: 'string',
|
|
123
|
+
description: 'Element id (e.g. "B1") or query string. Mutually exclusive with coords.',
|
|
124
|
+
},
|
|
125
|
+
coords: {
|
|
126
|
+
type: 'string',
|
|
127
|
+
description: 'Click coordinates as "x,y" (numbers). Mutually exclusive with on.',
|
|
128
|
+
},
|
|
129
|
+
wait: {
|
|
130
|
+
type: 'number',
|
|
131
|
+
description: 'Optional pre-click wait in milliseconds.',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
tier: 'free',
|
|
135
|
+
async execute(args) {
|
|
136
|
+
const binErr = await ensureBinary();
|
|
137
|
+
if (binErr)
|
|
138
|
+
return binErr;
|
|
139
|
+
const app = String(args.app ?? '');
|
|
140
|
+
if (!app)
|
|
141
|
+
return 'Error: app is required.';
|
|
142
|
+
const gate = requireApproval(app);
|
|
143
|
+
if (gate)
|
|
144
|
+
return gate;
|
|
145
|
+
const snapshot = String(args.snapshot ?? '');
|
|
146
|
+
if (!snapshot)
|
|
147
|
+
return 'Error: snapshot is required.';
|
|
148
|
+
const on = typeof args.on === 'string' && args.on.length > 0 ? args.on : undefined;
|
|
149
|
+
let coords;
|
|
150
|
+
if (typeof args.coords === 'string' && args.coords.length > 0) {
|
|
151
|
+
const parts = args.coords.split(',').map((p) => Number(p.trim()));
|
|
152
|
+
if (parts.length !== 2 || parts.some((n) => !Number.isFinite(n))) {
|
|
153
|
+
return 'Error: coords must be "x,y" (numbers).';
|
|
154
|
+
}
|
|
155
|
+
coords = [parts[0], parts[1]];
|
|
156
|
+
}
|
|
157
|
+
if (!on && !coords)
|
|
158
|
+
return 'Error: provide either `on` or `coords`.';
|
|
159
|
+
const wait = typeof args.wait === 'number' && Number.isFinite(args.wait) ? args.wait : undefined;
|
|
160
|
+
try {
|
|
161
|
+
const out = await click({
|
|
162
|
+
snapshot,
|
|
163
|
+
...(on ? { on } : {}),
|
|
164
|
+
...(coords ? { coords } : {}),
|
|
165
|
+
...(wait !== undefined ? { wait } : {}),
|
|
166
|
+
});
|
|
167
|
+
return outcomeToString(out);
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
return `Error: ${e.message}`;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
const peekabooType = {
|
|
175
|
+
name: 'peekaboo_type',
|
|
176
|
+
description: 'Type text into the focused field via the Peekaboo CLI. Use peekaboo_click to focus first.',
|
|
177
|
+
parameters: {
|
|
178
|
+
app: {
|
|
179
|
+
type: 'string',
|
|
180
|
+
description: 'App being targeted. Required for the approval gate.',
|
|
181
|
+
required: true,
|
|
182
|
+
},
|
|
183
|
+
text: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'Text to type.',
|
|
186
|
+
required: true,
|
|
187
|
+
},
|
|
188
|
+
clear: {
|
|
189
|
+
type: 'boolean',
|
|
190
|
+
description: 'Clear the field before typing (optional).',
|
|
191
|
+
},
|
|
192
|
+
delay_ms: {
|
|
193
|
+
type: 'number',
|
|
194
|
+
description: 'Per-character delay in milliseconds (optional).',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
tier: 'free',
|
|
198
|
+
async execute(args) {
|
|
199
|
+
const binErr = await ensureBinary();
|
|
200
|
+
if (binErr)
|
|
201
|
+
return binErr;
|
|
202
|
+
const app = String(args.app ?? '');
|
|
203
|
+
if (!app)
|
|
204
|
+
return 'Error: app is required.';
|
|
205
|
+
const gate = requireApproval(app);
|
|
206
|
+
if (gate)
|
|
207
|
+
return gate;
|
|
208
|
+
const text = typeof args.text === 'string' ? args.text : '';
|
|
209
|
+
if (!text)
|
|
210
|
+
return 'Error: text is required.';
|
|
211
|
+
const clear = args.clear === true;
|
|
212
|
+
const delayMs = typeof args.delay_ms === 'number' && Number.isFinite(args.delay_ms) ? args.delay_ms : undefined;
|
|
213
|
+
try {
|
|
214
|
+
const out = await type_({
|
|
215
|
+
text,
|
|
216
|
+
...(clear ? { clear: true } : {}),
|
|
217
|
+
...(delayMs !== undefined ? { delayMs } : {}),
|
|
218
|
+
});
|
|
219
|
+
return outcomeToString(out);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
return `Error: ${e.message}`;
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
const peekabooSetValue = {
|
|
227
|
+
name: 'peekaboo_set_value',
|
|
228
|
+
description: 'Set a settable AX value directly on an element (skips clicking). Faster than click+type for text fields, sliders, etc.',
|
|
229
|
+
parameters: {
|
|
230
|
+
app: {
|
|
231
|
+
type: 'string',
|
|
232
|
+
description: 'App being targeted. Required for the approval gate.',
|
|
233
|
+
required: true,
|
|
234
|
+
},
|
|
235
|
+
snapshot: {
|
|
236
|
+
type: 'string',
|
|
237
|
+
description: 'Snapshot id from peekaboo_see.',
|
|
238
|
+
required: true,
|
|
239
|
+
},
|
|
240
|
+
on: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
description: 'Target element id or query (must be settable).',
|
|
243
|
+
required: true,
|
|
244
|
+
},
|
|
245
|
+
value: {
|
|
246
|
+
type: 'string',
|
|
247
|
+
description: 'Value to assign.',
|
|
248
|
+
required: true,
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
tier: 'free',
|
|
252
|
+
async execute(args) {
|
|
253
|
+
const binErr = await ensureBinary();
|
|
254
|
+
if (binErr)
|
|
255
|
+
return binErr;
|
|
256
|
+
const app = String(args.app ?? '');
|
|
257
|
+
if (!app)
|
|
258
|
+
return 'Error: app is required.';
|
|
259
|
+
const gate = requireApproval(app);
|
|
260
|
+
if (gate)
|
|
261
|
+
return gate;
|
|
262
|
+
const snapshot = String(args.snapshot ?? '');
|
|
263
|
+
const on = String(args.on ?? '');
|
|
264
|
+
const value = typeof args.value === 'string' ? args.value : '';
|
|
265
|
+
if (!snapshot)
|
|
266
|
+
return 'Error: snapshot is required.';
|
|
267
|
+
if (!on)
|
|
268
|
+
return 'Error: on is required.';
|
|
269
|
+
try {
|
|
270
|
+
const out = await setValue({ snapshot, on, value });
|
|
271
|
+
return outcomeToString(out);
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
return `Error: ${e.message}`;
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
const peekabooPerformAction = {
|
|
279
|
+
name: 'peekaboo_perform_action',
|
|
280
|
+
description: 'Invoke a named AX action (e.g. AXPress, AXShowMenu) on an element from a Peekaboo snapshot.',
|
|
281
|
+
parameters: {
|
|
282
|
+
app: {
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'App being targeted. Required for the approval gate.',
|
|
285
|
+
required: true,
|
|
286
|
+
},
|
|
287
|
+
snapshot: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
description: 'Snapshot id from peekaboo_see.',
|
|
290
|
+
required: true,
|
|
291
|
+
},
|
|
292
|
+
on: {
|
|
293
|
+
type: 'string',
|
|
294
|
+
description: 'Target element id or query.',
|
|
295
|
+
required: true,
|
|
296
|
+
},
|
|
297
|
+
action: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'Named AX action (e.g. "AXPress", "AXShowMenu").',
|
|
300
|
+
required: true,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
tier: 'free',
|
|
304
|
+
async execute(args) {
|
|
305
|
+
const binErr = await ensureBinary();
|
|
306
|
+
if (binErr)
|
|
307
|
+
return binErr;
|
|
308
|
+
const app = String(args.app ?? '');
|
|
309
|
+
if (!app)
|
|
310
|
+
return 'Error: app is required.';
|
|
311
|
+
const gate = requireApproval(app);
|
|
312
|
+
if (gate)
|
|
313
|
+
return gate;
|
|
314
|
+
const snapshot = String(args.snapshot ?? '');
|
|
315
|
+
const on = String(args.on ?? '');
|
|
316
|
+
const action = String(args.action ?? '');
|
|
317
|
+
if (!snapshot)
|
|
318
|
+
return 'Error: snapshot is required.';
|
|
319
|
+
if (!on)
|
|
320
|
+
return 'Error: on is required.';
|
|
321
|
+
if (!action)
|
|
322
|
+
return 'Error: action is required.';
|
|
323
|
+
try {
|
|
324
|
+
const out = await performAction({ snapshot, on, action });
|
|
325
|
+
return outcomeToString(out);
|
|
326
|
+
}
|
|
327
|
+
catch (e) {
|
|
328
|
+
return `Error: ${e.message}`;
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
const peekabooAgent = {
|
|
333
|
+
name: 'peekaboo_agent',
|
|
334
|
+
description: "Hand a natural-language automation prompt to peekaboo's own agent subcommand. Returns the agent's stdout verbatim. Not gated by app approval — peekaboo's agent decides which apps to drive.",
|
|
335
|
+
parameters: {
|
|
336
|
+
prompt: {
|
|
337
|
+
type: 'string',
|
|
338
|
+
description: 'Free-form instruction for the peekaboo agent.',
|
|
339
|
+
required: true,
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
tier: 'free',
|
|
343
|
+
async execute(args) {
|
|
344
|
+
const binErr = await ensureBinary();
|
|
345
|
+
if (binErr)
|
|
346
|
+
return binErr;
|
|
347
|
+
const prompt = typeof args.prompt === 'string' ? args.prompt : '';
|
|
348
|
+
if (!prompt)
|
|
349
|
+
return 'Error: prompt is required.';
|
|
350
|
+
try {
|
|
351
|
+
const out = await agent({ prompt });
|
|
352
|
+
return outcomeToString(out);
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
return `Error: ${e.message}`;
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
export const peekabooTools = [
|
|
360
|
+
peekabooSee,
|
|
361
|
+
peekabooClick,
|
|
362
|
+
peekabooType,
|
|
363
|
+
peekabooSetValue,
|
|
364
|
+
peekabooPerformAction,
|
|
365
|
+
peekabooAgent,
|
|
366
|
+
];
|
|
367
|
+
export function registerPeekabooTools() {
|
|
368
|
+
for (const t of peekabooTools)
|
|
369
|
+
registerTool(t);
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=peekaboo.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ToolDefinition } from './index.js';
|
|
2
|
+
type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
|
|
3
|
+
export interface SurfaceSignal {
|
|
4
|
+
id: string;
|
|
5
|
+
category: string;
|
|
6
|
+
severity: Severity;
|
|
7
|
+
file: string;
|
|
8
|
+
line: number;
|
|
9
|
+
excerpt: string;
|
|
10
|
+
pattern: string;
|
|
11
|
+
ts: number;
|
|
12
|
+
}
|
|
13
|
+
export interface SurfaceMap {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
target: string;
|
|
16
|
+
startedAt: number;
|
|
17
|
+
filesWalked: number;
|
|
18
|
+
bytesRead: number;
|
|
19
|
+
signals: SurfaceSignal[];
|
|
20
|
+
skipped: {
|
|
21
|
+
path: string;
|
|
22
|
+
reason: string;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
export interface BuildSurfaceMapOptions {
|
|
26
|
+
target: string;
|
|
27
|
+
excludes?: Iterable<string>;
|
|
28
|
+
severityFloor?: Severity;
|
|
29
|
+
sessionId?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function buildSurfaceMap(opts: BuildSurfaceMapOptions): SurfaceMap;
|
|
32
|
+
export declare function persistSurfaceMap(map: SurfaceMap, baseDir?: string): string;
|
|
33
|
+
export declare function renderSurfaceMap(map: SurfaceMap, persistedTo: string): string;
|
|
34
|
+
export interface RunOptions {
|
|
35
|
+
target: string;
|
|
36
|
+
severityFloor?: Severity;
|
|
37
|
+
excludes?: string[];
|
|
38
|
+
baseDir?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function runSecurityAuditLocal(opts: RunOptions): {
|
|
41
|
+
map: SurfaceMap;
|
|
42
|
+
persistedTo: string;
|
|
43
|
+
markdown: string;
|
|
44
|
+
};
|
|
45
|
+
export declare const securityAuditLocalTool: ToolDefinition;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=security-audit-local.d.ts.map
|