hackmyagent 0.11.3 → 0.11.5
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/README.md +27 -10
- package/dist/cli.js +232 -36
- package/dist/cli.js.map +1 -1
- package/dist/hardening/index.d.ts +1 -0
- package/dist/hardening/index.d.ts.map +1 -1
- package/dist/hardening/index.js +4 -1
- package/dist/hardening/index.js.map +1 -1
- package/dist/hardening/nemoclaw-scanner.d.ts +46 -0
- package/dist/hardening/nemoclaw-scanner.d.ts.map +1 -0
- package/dist/hardening/nemoclaw-scanner.js +1061 -0
- package/dist/hardening/nemoclaw-scanner.js.map +1 -0
- package/dist/hardening/scanner.d.ts +7 -0
- package/dist/hardening/scanner.d.ts.map +1 -1
- package/dist/hardening/scanner.js +740 -13
- package/dist/hardening/scanner.js.map +1 -1
- package/dist/hardening/taxonomy.d.ts.map +1 -1
- package/dist/hardening/taxonomy.js +107 -65
- package/dist/hardening/taxonomy.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/telemetry/contribute.d.ts +58 -44
- package/dist/telemetry/contribute.d.ts.map +1 -1
- package/dist/telemetry/contribute.js +190 -96
- package/dist/telemetry/contribute.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +8 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/opt-in.d.ts +22 -13
- package/dist/telemetry/opt-in.d.ts.map +1 -1
- package/dist/telemetry/opt-in.js +93 -102
- package/dist/telemetry/opt-in.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Community Contribution Module
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Queue-based contribution of anonymized HMA scan summaries to the
|
|
6
|
+
* OpenA2A Registry. Compatible with @opena2a/contribute queue format:
|
|
7
|
+
* events queued by HMA are flushed by opena2a-cli and vice versa.
|
|
8
|
+
*
|
|
9
|
+
* Queue file: ~/.opena2a/contribute-queue.json
|
|
10
|
+
* Endpoint: POST api.oa2a.org/api/v1/contribute
|
|
11
|
+
*
|
|
12
|
+
* PRIVACY: Only summary statistics are sent (totalChecks, passed,
|
|
13
|
+
* severity counts, score, verdict). No file paths, no source code,
|
|
14
|
+
* no raw finding descriptions, no PII.
|
|
7
15
|
*/
|
|
8
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.generateContributorToken =
|
|
10
|
-
exports.
|
|
17
|
+
exports.generateContributorToken = void 0;
|
|
18
|
+
exports.getContributorToken = getContributorToken;
|
|
19
|
+
exports.queueEvent = queueEvent;
|
|
20
|
+
exports.buildScanEvent = buildScanEvent;
|
|
21
|
+
exports.queueAndMaybeFlush = queueAndMaybeFlush;
|
|
22
|
+
exports.flushQueue = flushQueue;
|
|
11
23
|
exports.buildContributionPayloadFromDir = buildContributionPayloadFromDir;
|
|
12
24
|
exports.submitContribution = submitContribution;
|
|
13
25
|
const crypto_1 = require("crypto");
|
|
@@ -15,20 +27,25 @@ const fs_1 = require("fs");
|
|
|
15
27
|
const os_1 = require("os");
|
|
16
28
|
const path_1 = require("path");
|
|
17
29
|
const index_1 = require("../index");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Paths and constants
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
const REGISTRY_URL = 'https://api.oa2a.org';
|
|
34
|
+
const FLUSH_THRESHOLD = 10;
|
|
35
|
+
const MAX_QUEUE_SIZE = 100;
|
|
36
|
+
const TIMEOUT_MS = 10000;
|
|
22
37
|
function getOpena2aHome() {
|
|
23
38
|
return process.env.OPENA2A_HOME || (0, path_1.join)(require('os').homedir(), '.opena2a');
|
|
24
39
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
function ensureDir() {
|
|
41
|
+
const dir = getOpena2aHome();
|
|
42
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
43
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Contributor token (stable per-device, SHA256-hashed)
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
function getContributorToken() {
|
|
32
49
|
const home = getOpena2aHome();
|
|
33
50
|
const saltPath = (0, path_1.join)(home, 'contributor-salt');
|
|
34
51
|
let salt;
|
|
@@ -37,28 +54,60 @@ function generateContributorToken() {
|
|
|
37
54
|
}
|
|
38
55
|
else {
|
|
39
56
|
salt = (0, crypto_1.randomBytes)(32).toString('hex');
|
|
40
|
-
(
|
|
57
|
+
ensureDir();
|
|
41
58
|
(0, fs_1.writeFileSync)(saltPath, salt, { mode: 0o600 });
|
|
42
59
|
}
|
|
43
60
|
const input = `${(0, os_1.hostname)()}|${(0, os_1.userInfo)().username}|${salt}`;
|
|
44
61
|
return (0, crypto_1.createHash)('sha256').update(input).digest('hex');
|
|
45
62
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
if (t === 'Darwin')
|
|
52
|
-
return 'macos';
|
|
53
|
-
if (t === 'Windows_NT')
|
|
54
|
-
return 'windows';
|
|
55
|
-
return 'linux';
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Queue operations (compatible with @opena2a/contribute queue format)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
function queuePath() {
|
|
67
|
+
return (0, path_1.join)(getOpena2aHome(), 'contribute-queue.json');
|
|
56
68
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
function loadQueue() {
|
|
70
|
+
const path = queuePath();
|
|
71
|
+
if (!(0, fs_1.existsSync)(path))
|
|
72
|
+
return { events: [] };
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { events: [] };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function saveQueue(queue) {
|
|
81
|
+
ensureDir();
|
|
82
|
+
(0, fs_1.writeFileSync)(queuePath(), JSON.stringify(queue), { mode: 0o600 });
|
|
83
|
+
}
|
|
84
|
+
function queueEvent(event) {
|
|
85
|
+
const queue = loadQueue();
|
|
86
|
+
queue.events.push(event);
|
|
87
|
+
if (queue.events.length > MAX_QUEUE_SIZE) {
|
|
88
|
+
queue.events = queue.events.slice(-MAX_QUEUE_SIZE);
|
|
89
|
+
}
|
|
90
|
+
saveQueue(queue);
|
|
91
|
+
}
|
|
92
|
+
function shouldFlush() {
|
|
93
|
+
return loadQueue().events.length >= FLUSH_THRESHOLD;
|
|
94
|
+
}
|
|
95
|
+
function buildBatch() {
|
|
96
|
+
const events = loadQueue().events;
|
|
97
|
+
if (events.length === 0)
|
|
98
|
+
return null;
|
|
99
|
+
return {
|
|
100
|
+
contributorToken: getContributorToken(),
|
|
101
|
+
events,
|
|
102
|
+
submittedAt: new Date().toISOString(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function clearQueue() {
|
|
106
|
+
saveQueue({ events: [] });
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Ecosystem and version detection
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
62
111
|
function detectEcosystem(directory) {
|
|
63
112
|
if ((0, fs_1.existsSync)((0, path_1.join)(directory, 'package.json')))
|
|
64
113
|
return 'npm';
|
|
@@ -68,102 +117,147 @@ function detectEcosystem(directory) {
|
|
|
68
117
|
}
|
|
69
118
|
return 'github';
|
|
70
119
|
}
|
|
71
|
-
|
|
72
|
-
* Build an anonymized contribution payload from scan findings.
|
|
73
|
-
*
|
|
74
|
-
* PRIVACY: This function intentionally strips all sensitive fields.
|
|
75
|
-
* The output contains ONLY: checkId, pass/fail result, and severity.
|
|
76
|
-
* No file paths, line numbers, descriptions, fix text, or code content.
|
|
77
|
-
*/
|
|
78
|
-
function buildContributionPayload(packageName, packageVersion, ecosystem, findings) {
|
|
79
|
-
const contributionFindings = findings.map((f) => ({
|
|
80
|
-
checkId: f.checkId,
|
|
81
|
-
result: f.passed ? 'pass' : 'fail',
|
|
82
|
-
severity: f.severity,
|
|
83
|
-
}));
|
|
84
|
-
return {
|
|
85
|
-
contributorToken: generateContributorToken(),
|
|
86
|
-
packageName,
|
|
87
|
-
packageVersion: packageVersion || '',
|
|
88
|
-
ecosystem,
|
|
89
|
-
scanTimestamp: new Date().toISOString(),
|
|
90
|
-
findings: contributionFindings,
|
|
91
|
-
hmaVersion: index_1.VERSION,
|
|
92
|
-
osType: resolveOsType(),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Build a contribution payload from scan findings, auto-detecting
|
|
97
|
-
* ecosystem and version from the target directory.
|
|
98
|
-
*
|
|
99
|
-
* Convenience wrapper around buildContributionPayload for CLI use.
|
|
100
|
-
*/
|
|
101
|
-
function buildContributionPayloadFromDir(packageName, directory, findings) {
|
|
102
|
-
let version = '';
|
|
103
|
-
const ecosystem = detectEcosystem(directory);
|
|
120
|
+
function detectPackageVersion(directory, ecosystem) {
|
|
104
121
|
try {
|
|
105
122
|
if (ecosystem === 'npm') {
|
|
106
123
|
const pkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(directory, 'package.json'), 'utf-8'));
|
|
107
|
-
|
|
124
|
+
return pkg.version || '';
|
|
108
125
|
}
|
|
109
|
-
|
|
110
|
-
// Best-effort version detection from setup.py or pyproject.toml
|
|
126
|
+
if (ecosystem === 'pypi') {
|
|
111
127
|
const setupPath = (0, path_1.join)(directory, 'setup.py');
|
|
112
128
|
if ((0, fs_1.existsSync)(setupPath)) {
|
|
113
129
|
const content = (0, fs_1.readFileSync)(setupPath, 'utf-8');
|
|
114
130
|
const match = content.match(/version\s*=\s*['"]([^'"]+)['"]/);
|
|
115
131
|
if (match)
|
|
116
|
-
|
|
132
|
+
return match[1];
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
136
|
catch {
|
|
121
137
|
// Non-fatal: version detection is best-effort
|
|
122
138
|
}
|
|
123
|
-
return
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Map OS type
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
function resolveOsType() {
|
|
145
|
+
const t = (0, os_1.type)();
|
|
146
|
+
if (t === 'Darwin')
|
|
147
|
+
return 'macos';
|
|
148
|
+
if (t === 'Windows_NT')
|
|
149
|
+
return 'windows';
|
|
150
|
+
return 'linux';
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Build contribution event from scan findings
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
function computeVerdict(findings) {
|
|
156
|
+
const critical = findings.filter(f => !f.passed && f.severity === 'critical').length;
|
|
157
|
+
const high = findings.filter(f => !f.passed && f.severity === 'high').length;
|
|
158
|
+
if (critical > 0)
|
|
159
|
+
return 'fail';
|
|
160
|
+
if (high > 0)
|
|
161
|
+
return 'warn';
|
|
162
|
+
return 'pass';
|
|
124
163
|
}
|
|
125
164
|
/**
|
|
126
|
-
*
|
|
165
|
+
* Build a ContributionEvent from HMA scan findings.
|
|
127
166
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
167
|
+
* Converts the detailed finding list into an anonymized summary:
|
|
168
|
+
* only counts and severity distribution, no file paths or descriptions.
|
|
130
169
|
*/
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
const
|
|
170
|
+
function buildScanEvent(packageName, directory, findings, durationMs) {
|
|
171
|
+
const ecosystem = detectEcosystem(directory);
|
|
172
|
+
const version = detectPackageVersion(directory, ecosystem);
|
|
173
|
+
const total = findings.length;
|
|
174
|
+
const passed = findings.filter(f => f.passed).length;
|
|
175
|
+
const failed = findings.filter(f => !f.passed);
|
|
176
|
+
return {
|
|
177
|
+
type: 'scan_result',
|
|
178
|
+
tool: 'hackmyagent',
|
|
179
|
+
toolVersion: index_1.VERSION,
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
package: {
|
|
182
|
+
name: packageName,
|
|
183
|
+
version: version || undefined,
|
|
184
|
+
ecosystem,
|
|
185
|
+
},
|
|
186
|
+
scanSummary: {
|
|
187
|
+
totalChecks: total,
|
|
188
|
+
passed,
|
|
189
|
+
critical: failed.filter(f => f.severity === 'critical').length,
|
|
190
|
+
high: failed.filter(f => f.severity === 'high').length,
|
|
191
|
+
medium: failed.filter(f => f.severity === 'medium').length,
|
|
192
|
+
low: failed.filter(f => f.severity === 'low').length,
|
|
193
|
+
score: total > 0 ? Math.round((passed / total) * 100) : 0,
|
|
194
|
+
verdict: computeVerdict(findings),
|
|
195
|
+
durationMs,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Submit: queue + flush
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
/**
|
|
203
|
+
* Queue a scan result and flush if threshold reached.
|
|
204
|
+
* Non-blocking, best-effort. Never throws.
|
|
205
|
+
*/
|
|
206
|
+
async function queueAndMaybeFlush(event, registryUrl, verbose) {
|
|
207
|
+
queueEvent(event);
|
|
208
|
+
if (shouldFlush()) {
|
|
209
|
+
await flushQueue(registryUrl, verbose);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Flush queued events to the OpenA2A Registry.
|
|
214
|
+
* Returns true if submission succeeded (or queue was empty).
|
|
215
|
+
*/
|
|
216
|
+
async function flushQueue(registryUrl, verbose) {
|
|
217
|
+
const batch = buildBatch();
|
|
218
|
+
if (!batch)
|
|
219
|
+
return true;
|
|
220
|
+
const url = `${(registryUrl || REGISTRY_URL).replace(/\/+$/, '')}/api/v1/contribute`;
|
|
134
221
|
try {
|
|
135
222
|
const controller = new AbortController();
|
|
136
|
-
const
|
|
223
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
137
224
|
const response = await fetch(url, {
|
|
138
225
|
method: 'POST',
|
|
139
226
|
headers: {
|
|
140
227
|
'Content-Type': 'application/json',
|
|
141
228
|
'User-Agent': `HackMyAgent-CLI/${index_1.VERSION}`,
|
|
142
229
|
},
|
|
143
|
-
body: JSON.stringify(
|
|
230
|
+
body: JSON.stringify(batch),
|
|
144
231
|
signal: controller.signal,
|
|
145
232
|
});
|
|
146
|
-
clearTimeout(
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
if (response.ok) {
|
|
235
|
+
clearQueue();
|
|
236
|
+
if (verbose) {
|
|
237
|
+
process.stderr.write(` Shared: anonymized results for ${batch.events.length} scan(s) (community trust)\n`);
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
153
240
|
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
success: true,
|
|
157
|
-
scanId: result.scanId || undefined,
|
|
158
|
-
};
|
|
241
|
+
return false;
|
|
159
242
|
}
|
|
160
|
-
catch
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (message.includes('abort') || message.includes('Abort')) {
|
|
164
|
-
return { success: false, error: 'Request timed out (10s)' };
|
|
165
|
-
}
|
|
166
|
-
return { success: false, error: message };
|
|
243
|
+
catch {
|
|
244
|
+
// Offline or unreachable -- events stay in queue for next time
|
|
245
|
+
return false;
|
|
167
246
|
}
|
|
168
247
|
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Legacy compatibility exports (used by existing code)
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
/** @deprecated Use buildScanEvent + queueAndMaybeFlush instead. */
|
|
252
|
+
function buildContributionPayloadFromDir(packageName, directory, findings) {
|
|
253
|
+
return buildScanEvent(packageName, directory, findings, 0);
|
|
254
|
+
}
|
|
255
|
+
/** @deprecated Use flushQueue instead. */
|
|
256
|
+
async function submitContribution(payload, registryUrl) {
|
|
257
|
+
queueEvent(payload);
|
|
258
|
+
const ok = await flushQueue(registryUrl);
|
|
259
|
+
return { success: ok };
|
|
260
|
+
}
|
|
261
|
+
/** @deprecated Kept for backward compat. */
|
|
262
|
+
exports.generateContributorToken = getContributorToken;
|
|
169
263
|
//# sourceMappingURL=contribute.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contribute.js","sourceRoot":"","sources":["../../src/telemetry/contribute.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"contribute.js","sourceRoot":"","sources":["../../src/telemetry/contribute.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;AAuEH,kDAeC;AAyBD,gCASC;AAqFD,wCAmCC;AAUD,gDAUC;AAMD,gCAwCC;AAOD,0EAMC;AAGD,gDAOC;AAvUD,mCAAiD;AACjD,2BAAwE;AACxE,2BAAwD;AACxD,+BAA4B;AAC5B,oCAAmC;AAGnC,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAC5C,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,UAAU,GAAG,KAAM,CAAC;AAE1B,SAAS,cAAc;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAA,WAAI,EAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC;QAAE,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AA0CD,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,SAAgB,mBAAmB;IACjC,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAEhD,IAAI,IAAY,CAAC;IACjB,IAAI,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,GAAG,IAAA,iBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvC,SAAS,EAAE,CAAC;QACZ,IAAA,kBAAa,EAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,IAAA,aAAQ,GAAE,IAAI,IAAA,aAAQ,GAAE,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;IAC7D,OAAO,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAE9E,SAAS,SAAS;IAChB,OAAO,IAAA,WAAI,EAAC,cAAc,EAAE,EAAE,uBAAuB,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC;IACzB,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAgB;IACjC,SAAS,EAAE,CAAC;IACZ,IAAA,kBAAa,EAAC,SAAS,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,SAAgB,UAAU,CAAC,KAAwB;IACjD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEzB,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QACzC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC;IACrD,CAAC;IAED,SAAS,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC,MAAM,IAAI,eAAe,CAAC;AACtD,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC,MAAM,CAAC;IAClC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,OAAO;QACL,gBAAgB,EAAE,mBAAmB,EAAE;QACvC,MAAM;QACN,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,SAAS,eAAe,CAAC,SAAiB;IACxC,IAAI,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,IACE,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QACvC,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,EAC7C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,oBAAoB,CAAC,SAAiB,EAAE,SAAiB;IAChE,IAAI,CAAC;QACH,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/E,OAAO,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;QAC3B,CAAC;QACD,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,UAAU,CAAC,CAAC;YAC9C,IAAI,IAAA,eAAU,EAAC,SAAS,CAAC,EAAE,CAAC;gBAC1B,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBAC9D,IAAI,KAAK;oBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,SAAS,aAAa;IACpB,MAAM,CAAC,GAAG,IAAA,SAAM,GAAE,CAAC;IACnB,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IACnC,IAAI,CAAC,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,8CAA8C;AAC9C,8EAA8E;AAE9E,SAAS,cAAc,CAAC,QAA2B;IACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IACrF,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAC7E,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,cAAc,CAC5B,WAAmB,EACnB,SAAiB,EACjB,QAA2B,EAC3B,UAAkB;IAElB,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,oBAAoB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAE3D,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAE/C,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,eAAO;QACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE;YACP,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,OAAO,IAAI,SAAS;YAC7B,SAAS;SACV;QACD,WAAW,EAAE;YACX,WAAW,EAAE,KAAK;YAClB,MAAM;YACN,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;YAC9D,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;YACtD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM;YAC1D,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM;YACpD,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC;YACjC,UAAU;SACX;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;GAGG;AACI,KAAK,UAAU,kBAAkB,CACtC,KAAwB,EACxB,WAAoB,EACpB,OAAiB;IAEjB,UAAU,CAAC,KAAK,CAAC,CAAC;IAElB,IAAI,WAAW,EAAE,EAAE,CAAC;QAClB,MAAM,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,UAAU,CAC9B,WAAoB,EACpB,OAAiB;IAEjB,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,oBAAoB,CAAC;IAErF,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QAE/D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,mBAAmB,eAAO,EAAE;aAC3C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAC3B,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,CAAC;QAEpB,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,UAAU,EAAE,CAAC;YACb,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oCAAoC,KAAK,CAAC,MAAM,CAAC,MAAM,8BAA8B,CACtF,CAAC;YACJ,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,+DAA+D;QAC/D,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,mEAAmE;AACnE,SAAgB,+BAA+B,CAC7C,WAAmB,EACnB,SAAiB,EACjB,QAA2B;IAE3B,OAAO,cAAc,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,0CAA0C;AACnC,KAAK,UAAU,kBAAkB,CACtC,OAA0B,EAC1B,WAAoB;IAEpB,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACzB,CAAC;AAED,4CAA4C;AAC/B,QAAA,wBAAwB,GAAG,mBAAmB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telemetry module -- community contribution of anonymized scan findings.
|
|
3
3
|
*/
|
|
4
|
-
export { generateContributorToken,
|
|
5
|
-
export { isContributeEnabled, shouldPromptContribute, incrementScanCount, saveContributeChoice, showContributePrompt, } from './opt-in';
|
|
4
|
+
export { getContributorToken, generateContributorToken, buildScanEvent, buildContributionPayloadFromDir, queueEvent, queueAndMaybeFlush, flushQueue, submitContribution, type ContributionEvent, type ContributionBatch, } from './contribute';
|
|
5
|
+
export { isContributeEnabled, shouldPromptContribute, incrementScanCount, saveContributeChoice, showContributePrompt, recordScanAndMaybeShowTip, _resetBackend, } from './opt-in';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,wBAAwB,EACxB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,cAAc,EACd,+BAA+B,EAC/B,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,aAAa,GACd,MAAM,UAAU,CAAC"}
|
package/dist/telemetry/index.js
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
* Telemetry module -- community contribution of anonymized scan findings.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.showContributePrompt = exports.saveContributeChoice = exports.incrementScanCount = exports.shouldPromptContribute = exports.isContributeEnabled = exports.submitContribution = exports.buildContributionPayloadFromDir = exports.
|
|
6
|
+
exports._resetBackend = exports.recordScanAndMaybeShowTip = exports.showContributePrompt = exports.saveContributeChoice = exports.incrementScanCount = exports.shouldPromptContribute = exports.isContributeEnabled = exports.submitContribution = exports.flushQueue = exports.queueAndMaybeFlush = exports.queueEvent = exports.buildContributionPayloadFromDir = exports.buildScanEvent = exports.generateContributorToken = exports.getContributorToken = void 0;
|
|
7
7
|
var contribute_1 = require("./contribute");
|
|
8
|
+
Object.defineProperty(exports, "getContributorToken", { enumerable: true, get: function () { return contribute_1.getContributorToken; } });
|
|
8
9
|
Object.defineProperty(exports, "generateContributorToken", { enumerable: true, get: function () { return contribute_1.generateContributorToken; } });
|
|
9
|
-
Object.defineProperty(exports, "
|
|
10
|
+
Object.defineProperty(exports, "buildScanEvent", { enumerable: true, get: function () { return contribute_1.buildScanEvent; } });
|
|
10
11
|
Object.defineProperty(exports, "buildContributionPayloadFromDir", { enumerable: true, get: function () { return contribute_1.buildContributionPayloadFromDir; } });
|
|
12
|
+
Object.defineProperty(exports, "queueEvent", { enumerable: true, get: function () { return contribute_1.queueEvent; } });
|
|
13
|
+
Object.defineProperty(exports, "queueAndMaybeFlush", { enumerable: true, get: function () { return contribute_1.queueAndMaybeFlush; } });
|
|
14
|
+
Object.defineProperty(exports, "flushQueue", { enumerable: true, get: function () { return contribute_1.flushQueue; } });
|
|
11
15
|
Object.defineProperty(exports, "submitContribution", { enumerable: true, get: function () { return contribute_1.submitContribution; } });
|
|
12
16
|
var opt_in_1 = require("./opt-in");
|
|
13
17
|
Object.defineProperty(exports, "isContributeEnabled", { enumerable: true, get: function () { return opt_in_1.isContributeEnabled; } });
|
|
@@ -15,4 +19,6 @@ Object.defineProperty(exports, "shouldPromptContribute", { enumerable: true, get
|
|
|
15
19
|
Object.defineProperty(exports, "incrementScanCount", { enumerable: true, get: function () { return opt_in_1.incrementScanCount; } });
|
|
16
20
|
Object.defineProperty(exports, "saveContributeChoice", { enumerable: true, get: function () { return opt_in_1.saveContributeChoice; } });
|
|
17
21
|
Object.defineProperty(exports, "showContributePrompt", { enumerable: true, get: function () { return opt_in_1.showContributePrompt; } });
|
|
22
|
+
Object.defineProperty(exports, "recordScanAndMaybeShowTip", { enumerable: true, get: function () { return opt_in_1.recordScanAndMaybeShowTip; } });
|
|
23
|
+
Object.defineProperty(exports, "_resetBackend", { enumerable: true, get: function () { return opt_in_1._resetBackend; } });
|
|
18
24
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,2CAWsB;AAVpB,iHAAA,mBAAmB,OAAA;AACnB,sHAAA,wBAAwB,OAAA;AACxB,4GAAA,cAAc,OAAA;AACd,6HAAA,+BAA+B,OAAA;AAC/B,wGAAA,UAAU,OAAA;AACV,gHAAA,kBAAkB,OAAA;AAClB,wGAAA,UAAU,OAAA;AACV,gHAAA,kBAAkB,OAAA;AAKpB,mCAQkB;AAPhB,6GAAA,mBAAmB,OAAA;AACnB,gHAAA,sBAAsB,OAAA;AACtB,4GAAA,kBAAkB,OAAA;AAClB,8GAAA,oBAAoB,OAAA;AACpB,8GAAA,oBAAoB,OAAA;AACpB,mHAAA,yBAAyB,OAAA;AACzB,uGAAA,aAAa,OAAA"}
|
|
@@ -1,46 +1,55 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Contribution
|
|
2
|
+
* Contribution Consent and Scan Counting
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* with the OpenA2A Registry.
|
|
4
|
+
* Manages the user's consent to share anonymized scan findings
|
|
5
|
+
* with the OpenA2A Registry. Uses a delayed consent tip shown
|
|
6
|
+
* after the 3rd scan (non-interactive, no blocking prompts).
|
|
6
7
|
*
|
|
7
8
|
* Config/counting is delegated to @opena2a/shared (the canonical
|
|
8
9
|
* source for ~/.opena2a/config.json). If @opena2a/shared is not
|
|
9
10
|
* available at runtime, falls back to a local implementation.
|
|
10
11
|
*/
|
|
12
|
+
/** Reset cached backend (for testing). */
|
|
13
|
+
export declare function _resetBackend(): void;
|
|
11
14
|
/**
|
|
12
15
|
* Check whether the contribution setting is enabled.
|
|
13
16
|
*
|
|
14
17
|
* Returns:
|
|
15
18
|
* true - user explicitly opted in
|
|
16
19
|
* false - user explicitly opted out (or default in shared backend)
|
|
17
|
-
* undefined - not yet configured
|
|
18
|
-
* defaults to false, so callers should rely on
|
|
19
|
-
* shouldPromptContribute() for prompt logic)
|
|
20
|
+
* undefined - not yet configured
|
|
20
21
|
*/
|
|
21
22
|
export declare function isContributeEnabled(): boolean | undefined;
|
|
22
23
|
/**
|
|
23
|
-
* Check whether we should show the contribution
|
|
24
|
+
* Check whether we should show the contribution tip.
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* and cooldown/dismiss logic.
|
|
26
|
+
* Returns true after the 3rd scan if the user hasn't opted in,
|
|
27
|
+
* opted out, or dismissed the tip within the last 30 days.
|
|
28
28
|
*/
|
|
29
29
|
export declare function shouldPromptContribute(): boolean;
|
|
30
30
|
/**
|
|
31
31
|
* Increment the scan count. Called after each scan completes,
|
|
32
32
|
* regardless of contribution setting.
|
|
33
33
|
*/
|
|
34
|
-
export declare function incrementScanCount():
|
|
34
|
+
export declare function incrementScanCount(): number;
|
|
35
35
|
/**
|
|
36
36
|
* Save the user's contribution choice to the config file.
|
|
37
37
|
*/
|
|
38
38
|
export declare function saveContributeChoice(enabled: boolean): void;
|
|
39
|
+
/**
|
|
40
|
+
* Record a scan and return a consent tip string if the threshold is reached.
|
|
41
|
+
*
|
|
42
|
+
* After the 3rd scan, returns a non-interactive tip encouraging the user
|
|
43
|
+
* to enable contribution. Returns null if tip should not be shown.
|
|
44
|
+
* This replaces the previous interactive Y/N prompt.
|
|
45
|
+
*/
|
|
46
|
+
export declare function recordScanAndMaybeShowTip(): string | null;
|
|
39
47
|
/**
|
|
40
48
|
* Display the contribution opt-in prompt and return the user's choice.
|
|
41
49
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
50
|
+
* @deprecated Use recordScanAndMaybeShowTip() instead. This is kept
|
|
51
|
+
* for backward compatibility but now shows a non-interactive tip
|
|
52
|
+
* rather than blocking for input.
|
|
44
53
|
*/
|
|
45
54
|
export declare function showContributePrompt(): Promise<boolean>;
|
|
46
55
|
//# sourceMappingURL=opt-in.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opt-in.d.ts","sourceRoot":"","sources":["../../src/telemetry/opt-in.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"opt-in.d.ts","sourceRoot":"","sources":["../../src/telemetry/opt-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH,0CAA0C;AAC1C,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AA2ID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,GAAG,SAAS,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK3D;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,GAAG,IAAI,CAezD;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAM7D"}
|