@zhixuan92/multi-model-agent 3.5.2 → 3.6.1
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/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +59 -0
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/telemetry.d.ts +10 -0
- package/dist/cli/telemetry.d.ts.map +1 -0
- package/dist/cli/telemetry.js +160 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/http/async-dispatch.js +1 -1
- package/dist/http/async-dispatch.js.map +1 -1
- package/dist/http/execution-context.d.ts +1 -1
- package/dist/http/execution-context.d.ts.map +1 -1
- package/dist/http/execution-context.js +15 -8
- package/dist/http/execution-context.js.map +1 -1
- package/dist/http/middleware/body-size.d.ts +7 -0
- package/dist/http/middleware/body-size.d.ts.map +1 -0
- package/dist/http/middleware/body-size.js +7 -0
- package/dist/http/middleware/body-size.js.map +1 -0
- package/dist/http/middleware/caller-identity.d.ts +11 -0
- package/dist/http/middleware/caller-identity.d.ts.map +1 -0
- package/dist/http/middleware/caller-identity.js +27 -0
- package/dist/http/middleware/caller-identity.js.map +1 -0
- package/dist/http/middleware/decompress.d.ts +14 -0
- package/dist/http/middleware/decompress.d.ts.map +1 -0
- package/dist/http/middleware/decompress.js +51 -0
- package/dist/http/middleware/decompress.js.map +1 -0
- package/dist/http/request-pipeline.d.ts.map +1 -1
- package/dist/http/request-pipeline.js +23 -3
- package/dist/http/request-pipeline.js.map +1 -1
- package/dist/http/server.d.ts +1 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +1 -1
- package/dist/http/server.js.map +1 -1
- package/dist/http/types.d.ts +3 -0
- package/dist/http/types.d.ts.map +1 -1
- package/dist/install/headers.d.ts +17 -0
- package/dist/install/headers.d.ts.map +1 -0
- package/dist/install/headers.js +22 -0
- package/dist/install/headers.js.map +1 -0
- package/dist/install/manifest-resolve.d.ts.map +1 -1
- package/dist/install/manifest-resolve.js +5 -0
- package/dist/install/manifest-resolve.js.map +1 -1
- package/dist/install/manifest.d.ts +2 -2
- package/dist/install/notify.d.ts +6 -0
- package/dist/install/notify.d.ts.map +1 -0
- package/dist/install/notify.js +16 -0
- package/dist/install/notify.js.map +1 -0
- package/dist/skills/mma-audit/SKILL.md +1 -1
- package/dist/skills/mma-clarifications/SKILL.md +1 -1
- package/dist/skills/mma-context-blocks/SKILL.md +1 -1
- package/dist/skills/mma-debug/SKILL.md +1 -1
- package/dist/skills/mma-delegate/SKILL.md +1 -1
- package/dist/skills/mma-execute-plan/SKILL.md +1 -1
- package/dist/skills/mma-investigate/SKILL.md +1 -1
- package/dist/skills/mma-retry/SKILL.md +1 -1
- package/dist/skills/mma-review/SKILL.md +1 -1
- package/dist/skills/mma-verify/SKILL.md +1 -1
- package/dist/skills/multi-model-agent/SKILL.md +1 -1
- package/dist/telemetry/consent.d.ts +4 -0
- package/dist/telemetry/consent.d.ts.map +1 -0
- package/dist/telemetry/consent.js +40 -0
- package/dist/telemetry/consent.js.map +1 -0
- package/dist/telemetry/flusher.d.ts +19 -0
- package/dist/telemetry/flusher.d.ts.map +1 -0
- package/dist/telemetry/flusher.js +231 -0
- package/dist/telemetry/flusher.js.map +1 -0
- package/dist/telemetry/generation.d.ts +9 -0
- package/dist/telemetry/generation.d.ts.map +1 -0
- package/dist/telemetry/generation.js +33 -0
- package/dist/telemetry/generation.js.map +1 -0
- package/dist/telemetry/install-id.d.ts +13 -0
- package/dist/telemetry/install-id.d.ts.map +1 -0
- package/dist/telemetry/install-id.js +49 -0
- package/dist/telemetry/install-id.js.map +1 -0
- package/dist/telemetry/install-meta.d.ts +6 -0
- package/dist/telemetry/install-meta.d.ts.map +1 -0
- package/dist/telemetry/install-meta.js +38 -0
- package/dist/telemetry/install-meta.js.map +1 -0
- package/dist/telemetry/notice.d.ts +9 -0
- package/dist/telemetry/notice.d.ts.map +1 -0
- package/dist/telemetry/notice.js +91 -0
- package/dist/telemetry/notice.js.map +1 -0
- package/dist/telemetry/queue.d.ts +34 -0
- package/dist/telemetry/queue.d.ts.map +1 -0
- package/dist/telemetry/queue.js +239 -0
- package/dist/telemetry/queue.js.map +1 -0
- package/dist/telemetry/recorder.d.ts +18 -0
- package/dist/telemetry/recorder.d.ts.map +1 -0
- package/dist/telemetry/recorder.js +123 -0
- package/dist/telemetry/recorder.js.map +1 -0
- package/package.json +5 -3
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { gzipSync } from 'node:zlib';
|
|
2
|
+
import { readGeneration } from './generation.js';
|
|
3
|
+
const MAX_BATCH = 500;
|
|
4
|
+
const INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
5
|
+
const BOOT_DELAY_MS = 5_000; // 5 seconds
|
|
6
|
+
const QUEUE_SIZE_TRIGGER = 100;
|
|
7
|
+
const DRAIN_BUDGET_MS = 2_000; // 2 seconds
|
|
8
|
+
const MAX_BACKOFF_MS = 60 * 60 * 1000; // 1 hour cap
|
|
9
|
+
const NO_RETRY_AFTER_DEFAULT = 60 * 60 * 1000; // 1 hour
|
|
10
|
+
const INITIAL_BACKOFF_MS = 5 * 60 * 1000; // 5 minutes
|
|
11
|
+
function groupKey(record) {
|
|
12
|
+
return `${record.schemaVersion}|${record.install.installId}|${record.install.mmagentVersion}|${record.install.os}|${record.install.nodeMajor}|${record.install.language}|${record.install.tzOffsetBucket}|${record.generation}`;
|
|
13
|
+
}
|
|
14
|
+
export class Flusher {
|
|
15
|
+
#queue;
|
|
16
|
+
#dir;
|
|
17
|
+
#endpoint;
|
|
18
|
+
#controller;
|
|
19
|
+
#timer = null;
|
|
20
|
+
#bootTimer = null;
|
|
21
|
+
#backoffTimer = null;
|
|
22
|
+
#backoffMs = 0;
|
|
23
|
+
#inFlight = false;
|
|
24
|
+
#dropped = 0;
|
|
25
|
+
constructor(opts) {
|
|
26
|
+
this.#queue = opts.queue;
|
|
27
|
+
this.#dir = opts.dir;
|
|
28
|
+
this.#endpoint = opts.endpoint;
|
|
29
|
+
this.#controller = new AbortController();
|
|
30
|
+
}
|
|
31
|
+
get controller() {
|
|
32
|
+
return this.#controller;
|
|
33
|
+
}
|
|
34
|
+
get dropped() {
|
|
35
|
+
return this.#dropped;
|
|
36
|
+
}
|
|
37
|
+
get backoffActive() {
|
|
38
|
+
return this.#backoffTimer !== null;
|
|
39
|
+
}
|
|
40
|
+
start() {
|
|
41
|
+
this.#timer = setInterval(() => {
|
|
42
|
+
if (!this.#inFlight && !this.backoffActive) {
|
|
43
|
+
this.flush().catch(() => { });
|
|
44
|
+
}
|
|
45
|
+
}, INTERVAL_MS);
|
|
46
|
+
this.#timer.unref();
|
|
47
|
+
this.#bootTimer = setTimeout(() => {
|
|
48
|
+
this.#bootTimer = null;
|
|
49
|
+
this.flush().catch(() => { });
|
|
50
|
+
}, BOOT_DELAY_MS);
|
|
51
|
+
this.#bootTimer.unref();
|
|
52
|
+
}
|
|
53
|
+
stop() {
|
|
54
|
+
if (this.#timer) {
|
|
55
|
+
clearInterval(this.#timer);
|
|
56
|
+
this.#timer = null;
|
|
57
|
+
}
|
|
58
|
+
if (this.#bootTimer) {
|
|
59
|
+
clearTimeout(this.#bootTimer);
|
|
60
|
+
this.#bootTimer = null;
|
|
61
|
+
}
|
|
62
|
+
this.clearBackoff();
|
|
63
|
+
this.#controller.abort();
|
|
64
|
+
}
|
|
65
|
+
async drain() {
|
|
66
|
+
this.stop();
|
|
67
|
+
const deadline = Date.now() + DRAIN_BUDGET_MS;
|
|
68
|
+
const drainSignal = AbortSignal.timeout(DRAIN_BUDGET_MS);
|
|
69
|
+
try {
|
|
70
|
+
await this.#doFlush(drainSignal, deadline);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// drain is best-effort
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async flush() {
|
|
77
|
+
if (this.#inFlight)
|
|
78
|
+
return;
|
|
79
|
+
if (this.backoffActive)
|
|
80
|
+
return;
|
|
81
|
+
return this.#doFlush(this.#controller.signal);
|
|
82
|
+
}
|
|
83
|
+
async #doFlush(signal, deadline) {
|
|
84
|
+
if (signal.aborted)
|
|
85
|
+
return;
|
|
86
|
+
this.#inFlight = true;
|
|
87
|
+
try {
|
|
88
|
+
// Step 1: read up to 500 records + capture generation snapshot
|
|
89
|
+
const batch = await this.#queue.readBatch(MAX_BATCH);
|
|
90
|
+
if (batch.records.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const genSnapshot = readGeneration(this.#dir);
|
|
93
|
+
// Step 4: Group consecutive records by (schemaVersion, install, generation)
|
|
94
|
+
const groups = [];
|
|
95
|
+
let currentKey = '';
|
|
96
|
+
let currentRecords = [];
|
|
97
|
+
let currentMeta = [];
|
|
98
|
+
for (let i = 0; i < batch.records.length; i++) {
|
|
99
|
+
const key = groupKey(batch.records[i]);
|
|
100
|
+
if (currentKey && key !== currentKey) {
|
|
101
|
+
groups.push({ records: currentRecords, meta: currentMeta });
|
|
102
|
+
currentRecords = [];
|
|
103
|
+
currentMeta = [];
|
|
104
|
+
}
|
|
105
|
+
currentKey = key;
|
|
106
|
+
currentRecords.push(batch.records[i]);
|
|
107
|
+
currentMeta.push(batch.meta[i]);
|
|
108
|
+
}
|
|
109
|
+
if (currentRecords.length > 0) {
|
|
110
|
+
groups.push({ records: currentRecords, meta: currentMeta });
|
|
111
|
+
}
|
|
112
|
+
// Steps 5-6: Process each batch in order
|
|
113
|
+
let acknowledgedCount = 0;
|
|
114
|
+
let shouldBackoff = false;
|
|
115
|
+
let backoffDuration = 0;
|
|
116
|
+
for (const group of groups) {
|
|
117
|
+
if (deadline && Date.now() > deadline)
|
|
118
|
+
break;
|
|
119
|
+
// Re-check generation; if changed since read, abort
|
|
120
|
+
const currentGen = readGeneration(this.#dir);
|
|
121
|
+
if (currentGen !== genSnapshot)
|
|
122
|
+
break;
|
|
123
|
+
if (signal.aborted)
|
|
124
|
+
break;
|
|
125
|
+
const result = await this.#uploadBatch(group, signal);
|
|
126
|
+
if (result.status === '204' || result.status === '400' || result.status === '413') {
|
|
127
|
+
acknowledgedCount += group.records.length;
|
|
128
|
+
if (result.status === '400' || result.status === '413') {
|
|
129
|
+
this.#dropped += group.records.length;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
shouldBackoff = true;
|
|
134
|
+
if (result.status === '429') {
|
|
135
|
+
backoffDuration = result.retryAfterSeconds !== null
|
|
136
|
+
? result.retryAfterSeconds * 1000
|
|
137
|
+
: NO_RETRY_AFTER_DEFAULT;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
backoffDuration = this.#nextBackoff();
|
|
141
|
+
}
|
|
142
|
+
break; // stop iterating on non-success
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Steps 7-9: truncate acknowledged prefix
|
|
146
|
+
if (acknowledgedCount > 0) {
|
|
147
|
+
await this.#queue.truncate(batch.meta.slice(0, acknowledgedCount));
|
|
148
|
+
}
|
|
149
|
+
// Apply or clear backoff
|
|
150
|
+
if (shouldBackoff) {
|
|
151
|
+
this.#scheduleBackoff(backoffDuration);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Reset backoff on successful drain (all batches processed)
|
|
155
|
+
this.clearBackoff();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
this.#inFlight = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async #uploadBatch(group, signal) {
|
|
163
|
+
const first = group.records[0];
|
|
164
|
+
const events = group.records.map(r => r.event);
|
|
165
|
+
const body = gzipSync(JSON.stringify({
|
|
166
|
+
schemaVersion: first.schemaVersion,
|
|
167
|
+
install: first.install,
|
|
168
|
+
events,
|
|
169
|
+
}));
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(this.#endpoint, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: {
|
|
174
|
+
'Content-Encoding': 'gzip',
|
|
175
|
+
'Content-Type': 'application/json',
|
|
176
|
+
},
|
|
177
|
+
body,
|
|
178
|
+
signal,
|
|
179
|
+
});
|
|
180
|
+
const status = response.status;
|
|
181
|
+
if (status === 204)
|
|
182
|
+
return { status: '204', retryAfterSeconds: null };
|
|
183
|
+
if (status === 400)
|
|
184
|
+
return { status: '400', retryAfterSeconds: null };
|
|
185
|
+
if (status === 413)
|
|
186
|
+
return { status: '413', retryAfterSeconds: null };
|
|
187
|
+
if (status === 429) {
|
|
188
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
189
|
+
const seconds = retryAfter ? parseInt(retryAfter, 10) : null;
|
|
190
|
+
return { status: '429', retryAfterSeconds: Number.isFinite(seconds) ? seconds : null };
|
|
191
|
+
}
|
|
192
|
+
if (status >= 500)
|
|
193
|
+
return { status: '5xx', retryAfterSeconds: null };
|
|
194
|
+
// Unexpected status: treat as 5xx
|
|
195
|
+
return { status: '5xx', retryAfterSeconds: null };
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
return { status: 'network', retryAfterSeconds: null };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
#nextBackoff() {
|
|
205
|
+
if (this.#backoffMs === 0) {
|
|
206
|
+
this.#backoffMs = INITIAL_BACKOFF_MS;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
this.#backoffMs = Math.min(this.#backoffMs * 2, MAX_BACKOFF_MS);
|
|
210
|
+
}
|
|
211
|
+
return this.#backoffMs;
|
|
212
|
+
}
|
|
213
|
+
#scheduleBackoff(ms) {
|
|
214
|
+
this.clearBackoff();
|
|
215
|
+
this.#backoffTimer = setTimeout(() => {
|
|
216
|
+
this.#backoffTimer = null;
|
|
217
|
+
this.#backoffMs = 0;
|
|
218
|
+
this.flush().catch(() => { });
|
|
219
|
+
}, ms);
|
|
220
|
+
if (this.#backoffTimer.unref)
|
|
221
|
+
this.#backoffTimer.unref();
|
|
222
|
+
}
|
|
223
|
+
clearBackoff() {
|
|
224
|
+
if (this.#backoffTimer) {
|
|
225
|
+
clearTimeout(this.#backoffTimer);
|
|
226
|
+
this.#backoffTimer = null;
|
|
227
|
+
}
|
|
228
|
+
this.#backoffMs = 0;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=flusher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusher.js","sourceRoot":"","sources":["../../src/telemetry/flusher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function readGeneration(dir: string): number;
|
|
2
|
+
/**
|
|
3
|
+
* Atomic increment. Uses proper-lockfile (already a dep for queue.ts) so two
|
|
4
|
+
* simultaneous `mmagent telemetry disable` invocations cannot both read N and
|
|
5
|
+
* both write N+1 (which would silently lose a generation bump and leave a
|
|
6
|
+
* revoked identity's events accepted by the backend).
|
|
7
|
+
*/
|
|
8
|
+
export declare function bumpGeneration(dir: string): Promise<number>;
|
|
9
|
+
//# sourceMappingURL=generation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/telemetry/generation.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYjE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import lockfile from 'proper-lockfile';
|
|
4
|
+
const FILE = 'telemetry-generation';
|
|
5
|
+
export function readGeneration(dir) {
|
|
6
|
+
const p = join(dir, FILE);
|
|
7
|
+
if (!existsSync(p))
|
|
8
|
+
return 0;
|
|
9
|
+
const n = Number.parseInt(readFileSync(p, 'utf8').trim(), 10);
|
|
10
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Atomic increment. Uses proper-lockfile (already a dep for queue.ts) so two
|
|
14
|
+
* simultaneous `mmagent telemetry disable` invocations cannot both read N and
|
|
15
|
+
* both write N+1 (which would silently lose a generation bump and leave a
|
|
16
|
+
* revoked identity's events accepted by the backend).
|
|
17
|
+
*/
|
|
18
|
+
export async function bumpGeneration(dir) {
|
|
19
|
+
const p = join(dir, FILE);
|
|
20
|
+
if (!existsSync(p))
|
|
21
|
+
writeFileSync(p, '0', { mode: 0o600 });
|
|
22
|
+
const release = await lockfile.lock(p, { retries: { retries: 15, minTimeout: 50, maxTimeout: 500 } });
|
|
23
|
+
try {
|
|
24
|
+
const current = readGeneration(dir);
|
|
25
|
+
const next = current + 1;
|
|
26
|
+
writeFileSync(p, String(next), { mode: 0o600 });
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
await release();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=generation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/telemetry/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,QAAQ,MAAM,iBAAiB,CAAC;AAEvC,MAAM,IAAI,GAAG,sBAAsB,CAAC;AAEpC,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtG,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,CAAC;QACzB,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function hasInstallId(dir: string): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* Atomic create-or-read.
|
|
4
|
+
* - `openSync(path, 'wx')` is exclusive — succeeds only if the file does not exist.
|
|
5
|
+
* - Two concurrent processes racing here: one wins the create, the other gets EEXIST
|
|
6
|
+
* and falls through to the read path. No `proper-lockfile` needed for this one.
|
|
7
|
+
* - We `fsyncSync` the WRITE descriptor (not a re-opened read fd) so the kernel
|
|
8
|
+
* actually flushes the bytes; this is the data-durability guarantee that survives
|
|
9
|
+
* power loss.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getOrCreateInstallId(dir: string): string;
|
|
12
|
+
export declare function deleteInstallId(dir: string): void;
|
|
13
|
+
//# sourceMappingURL=install-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-id.d.ts","sourceRoot":"","sources":["../../src/telemetry/install-id.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAwBxD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAGjD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readFileSync, openSync, closeSync, writeSync, fsyncSync, existsSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
const FILE = 'install-id';
|
|
5
|
+
export function hasInstallId(dir) {
|
|
6
|
+
return existsSync(join(dir, FILE));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Atomic create-or-read.
|
|
10
|
+
* - `openSync(path, 'wx')` is exclusive — succeeds only if the file does not exist.
|
|
11
|
+
* - Two concurrent processes racing here: one wins the create, the other gets EEXIST
|
|
12
|
+
* and falls through to the read path. No `proper-lockfile` needed for this one.
|
|
13
|
+
* - We `fsyncSync` the WRITE descriptor (not a re-opened read fd) so the kernel
|
|
14
|
+
* actually flushes the bytes; this is the data-durability guarantee that survives
|
|
15
|
+
* power loss.
|
|
16
|
+
*/
|
|
17
|
+
export function getOrCreateInstallId(dir) {
|
|
18
|
+
const path = join(dir, FILE);
|
|
19
|
+
// Fast path: file already exists.
|
|
20
|
+
if (existsSync(path)) {
|
|
21
|
+
return readFileSync(path, 'utf8').trim();
|
|
22
|
+
}
|
|
23
|
+
// Race-safe create.
|
|
24
|
+
const id = randomUUID();
|
|
25
|
+
let fd;
|
|
26
|
+
try {
|
|
27
|
+
fd = openSync(path, 'wx', 0o600); // EEXIST if a concurrent process beat us
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (e.code === 'EEXIST') {
|
|
31
|
+
return readFileSync(path, 'utf8').trim();
|
|
32
|
+
}
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
writeSync(fd, id);
|
|
37
|
+
fsyncSync(fd); // flush data to durable storage on the WRITE fd
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
closeSync(fd);
|
|
41
|
+
}
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
44
|
+
export function deleteInstallId(dir) {
|
|
45
|
+
const path = join(dir, FILE);
|
|
46
|
+
if (existsSync(path))
|
|
47
|
+
unlinkSync(path);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=install-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-id.js","sourceRoot":"","sources":["../../src/telemetry/install-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1G,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,IAAI,GAAG,YAAY,CAAC;AAE1B,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7B,kCAAkC;IAClC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;IACD,oBAAoB;IACpB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,yCAAyC;IAC9E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAK,CAA2B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnD,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,CAAC;QACD,MAAM,CAAC,CAAC;IACV,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAClB,SAAS,CAAC,EAAE,CAAC,CAAC,CAAE,gDAAgD;IAClE,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7B,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-meta.d.ts","sourceRoot":"","sources":["../../src/telemetry/install-meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mDAAmD,CAAC;AA4B7F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,mBAAmB,CAStB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const KNOWN_LANG = new Set([
|
|
2
|
+
'en', 'es', 'fr', 'de', 'zh', 'ja', 'ko', 'pt', 'ru', 'it',
|
|
3
|
+
'tr', 'ar', 'hi', 'vi', 'id', 'th', 'pl', 'nl', 'sv',
|
|
4
|
+
]);
|
|
5
|
+
function bucketTz() {
|
|
6
|
+
const utcHours = -new Date().getTimezoneOffset() / 60;
|
|
7
|
+
if (utcHours <= -6)
|
|
8
|
+
return 'utc_minus_12_to_minus_6';
|
|
9
|
+
if (utcHours < 0)
|
|
10
|
+
return 'utc_minus_6_to_0';
|
|
11
|
+
if (utcHours < 6)
|
|
12
|
+
return 'utc_0_to_plus_6';
|
|
13
|
+
if (utcHours < 12)
|
|
14
|
+
return 'utc_plus_6_to_plus_12';
|
|
15
|
+
return 'utc_plus_12_to_plus_15';
|
|
16
|
+
}
|
|
17
|
+
function bucketLang() {
|
|
18
|
+
const raw = (process.env.LANG ?? Intl.DateTimeFormat().resolvedOptions().locale ?? '').toLowerCase();
|
|
19
|
+
const two = raw.split(/[._-]/)[0] ?? '';
|
|
20
|
+
return KNOWN_LANG.has(two) ? two : 'other';
|
|
21
|
+
}
|
|
22
|
+
function bucketOs() {
|
|
23
|
+
const p = process.platform;
|
|
24
|
+
if (p === 'darwin' || p === 'linux' || p === 'win32')
|
|
25
|
+
return p;
|
|
26
|
+
return 'other';
|
|
27
|
+
}
|
|
28
|
+
export function buildInstallMeta(args) {
|
|
29
|
+
return {
|
|
30
|
+
installId: args.installId,
|
|
31
|
+
mmagentVersion: args.mmagentVersion,
|
|
32
|
+
os: bucketOs(),
|
|
33
|
+
nodeMajor: String(process.versions.node.split('.')[0]),
|
|
34
|
+
language: bucketLang(),
|
|
35
|
+
tzOffsetBucket: bucketTz(),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=install-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-meta.js","sourceRoot":"","sources":["../../src/telemetry/install-meta.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;IAC1D,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;CACrD,CAAC,CAAC;AAEH,SAAS,QAAQ;IACf,MAAM,QAAQ,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC;IACtD,IAAI,QAAQ,IAAI,CAAC,CAAC;QAAE,OAAO,yBAAyB,CAAC;IACrD,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAC5C,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,iBAAiB,CAAC;IAC3C,IAAI,QAAQ,GAAG,EAAE;QAAE,OAAO,uBAAuB,CAAC;IAClD,OAAO,wBAAwB,CAAC;AAClC,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACrG,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,OAAO,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAAW,CAAC,CAAC,CAAC,OAAO,CAAC;AACtD,CAAC;AAED,SAAS,QAAQ;IACf,MAAM,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC3B,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,CAAC,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAGhC;IACC,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,EAAE,EAAE,QAAQ,EAAE;QACd,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,QAAQ,EAAE,UAAU,EAAE;QACtB,cAAc,EAAE,QAAQ,EAAE;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConsentDecision } from '@zhixuan92/multi-model-agent-core/telemetry/consent-rules';
|
|
2
|
+
export type NoticeWriter = (message: string) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Show the first-run telemetry banner to stderr if the notice flag is absent.
|
|
5
|
+
* The flag is created ONLY after the banner write succeeds.
|
|
6
|
+
* Does NOT call getOrCreateInstallId — the banner predates identity.
|
|
7
|
+
*/
|
|
8
|
+
export declare function showNotice(dir: string, decision: ConsentDecision, write?: NoticeWriter): void;
|
|
9
|
+
//# sourceMappingURL=notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notice.d.ts","sourceRoot":"","sources":["../../src/telemetry/notice.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2DAA2D,CAAC;AAMjG,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAwErD;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,eAAe,EACzB,KAAK,GAAE,YAAgE,GACtE,IAAI,CAWN"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const FLAG_FILE = 'telemetry-notice-shown';
|
|
4
|
+
const HR = '────────────────────────────────────────────────────────────────────────────';
|
|
5
|
+
const PRIVACY_URL = 'https://github.com/zhixuan312/multi-model-agent/blob/main/docs/PRIVACY.md';
|
|
6
|
+
function composeBanner(decision) {
|
|
7
|
+
if (decision.enabled) {
|
|
8
|
+
return [
|
|
9
|
+
HR,
|
|
10
|
+
' mmagent telemetry is ENABLED. Thanks for opting in.',
|
|
11
|
+
'',
|
|
12
|
+
' We collect pseudonymous, low-cardinality usage statistics',
|
|
13
|
+
' (route usage, model families, task outcomes, bucketed costs/durations).',
|
|
14
|
+
' Never prompts, files, or paths.',
|
|
15
|
+
'',
|
|
16
|
+
' Opt out anytime:',
|
|
17
|
+
' mmagent telemetry disable',
|
|
18
|
+
' or set MMAGENT_TELEMETRY=0',
|
|
19
|
+
' or add "telemetry": { "enabled": false } to ~/.multi-model/config.json',
|
|
20
|
+
'',
|
|
21
|
+
` Full details + every field we collect: ${PRIVACY_URL}`,
|
|
22
|
+
HR,
|
|
23
|
+
].join('\n');
|
|
24
|
+
}
|
|
25
|
+
if (decision.source === 'default') {
|
|
26
|
+
return [
|
|
27
|
+
HR,
|
|
28
|
+
' mmagent telemetry is OFF by default in this release (3.6.0).',
|
|
29
|
+
'',
|
|
30
|
+
' When enabled, mmagent collects pseudonymous, low-cardinality usage stats',
|
|
31
|
+
' (route usage, model families, task outcomes, bucketed costs/durations).',
|
|
32
|
+
' Never prompts, files, or paths.',
|
|
33
|
+
'',
|
|
34
|
+
' To opt IN and help us improve the tool:',
|
|
35
|
+
' mmagent telemetry enable',
|
|
36
|
+
' or set MMAGENT_TELEMETRY=1',
|
|
37
|
+
' or add "telemetry": { "enabled": true } to ~/.multi-model/config.json',
|
|
38
|
+
'',
|
|
39
|
+
` Full details + every field we'd collect: ${PRIVACY_URL}`,
|
|
40
|
+
HR,
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
|
43
|
+
let extra = '';
|
|
44
|
+
if (decision.source === 'env_invalid') {
|
|
45
|
+
extra =
|
|
46
|
+
'\n' +
|
|
47
|
+
[
|
|
48
|
+
'',
|
|
49
|
+
' If source is env_invalid: your MMAGENT_TELEMETRY env var has an unrecognized',
|
|
50
|
+
' value (e.g. "fales"). Telemetry is disabled fail-closed for safety. Use',
|
|
51
|
+
' "MMAGENT_TELEMETRY=0" to opt out, or "MMAGENT_TELEMETRY=1" to enable.',
|
|
52
|
+
].join('\n');
|
|
53
|
+
}
|
|
54
|
+
else if (decision.source === 'config_unreadable') {
|
|
55
|
+
extra =
|
|
56
|
+
'\n' +
|
|
57
|
+
[
|
|
58
|
+
'',
|
|
59
|
+
' If source is config_unreadable: ~/.multi-model/config.json could not be parsed.',
|
|
60
|
+
' Fix the file or remove it.',
|
|
61
|
+
].join('\n');
|
|
62
|
+
}
|
|
63
|
+
return [
|
|
64
|
+
HR,
|
|
65
|
+
` mmagent telemetry is currently DISABLED (source: ${decision.source}).${extra}`,
|
|
66
|
+
'',
|
|
67
|
+
' No anonymous usage data is collected from this install.',
|
|
68
|
+
' Re-enable: mmagent telemetry enable',
|
|
69
|
+
` Details: ${PRIVACY_URL}`,
|
|
70
|
+
HR,
|
|
71
|
+
].join('\n');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Show the first-run telemetry banner to stderr if the notice flag is absent.
|
|
75
|
+
* The flag is created ONLY after the banner write succeeds.
|
|
76
|
+
* Does NOT call getOrCreateInstallId — the banner predates identity.
|
|
77
|
+
*/
|
|
78
|
+
export function showNotice(dir, decision, write = (msg) => process.stderr.write(msg + '\n')) {
|
|
79
|
+
const flagPath = join(dir, FLAG_FILE);
|
|
80
|
+
if (existsSync(flagPath))
|
|
81
|
+
return;
|
|
82
|
+
const banner = composeBanner(decision);
|
|
83
|
+
try {
|
|
84
|
+
write(banner);
|
|
85
|
+
writeFileSync(flagPath, '', { mode: 0o644 });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// If write fails, no flag is created — banner shown again next boot.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notice.js","sourceRoot":"","sources":["../../src/telemetry/notice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,SAAS,GAAG,wBAAwB,CAAC;AAC3C,MAAM,EAAE,GAAG,8EAA8E,CAAC;AAC1F,MAAM,WAAW,GAAG,2EAA2E,CAAC;AAIhG,SAAS,aAAa,CAAC,QAAyB;IAC9C,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO;YACL,EAAE;YACF,sDAAsD;YACtD,EAAE;YACF,4DAA4D;YAC5D,0EAA0E;YAC1E,kCAAkC;YAClC,EAAE;YACF,mBAAmB;YACnB,8BAA8B;YAC9B,+BAA+B;YAC/B,6EAA6E;YAC7E,EAAE;YACF,2CAA2C,WAAW,EAAE;YACxD,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO;YACL,EAAE;YACF,+DAA+D;YAC/D,EAAE;YACF,2EAA2E;YAC3E,0EAA0E;YAC1E,kCAAkC;YAClC,EAAE;YACF,0CAA0C;YAC1C,6BAA6B;YAC7B,+BAA+B;YAC/B,4EAA4E;YAC5E,EAAE;YACF,6CAA6C,WAAW,EAAE;YAC1D,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACtC,KAAK;YACH,IAAI;gBACJ;oBACE,EAAE;oBACF,+EAA+E;oBAC/E,0EAA0E;oBAC1E,wEAAwE;iBACzE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;SAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,mBAAmB,EAAE,CAAC;QACnD,KAAK;YACH,IAAI;gBACJ;oBACE,EAAE;oBACF,kFAAkF;oBAClF,6BAA6B;iBAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,OAAO;QACL,EAAE;QACF,qDAAqD,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE;QAChF,EAAE;QACF,0DAA0D;QAC1D,sCAAsC;QACtC,eAAe,WAAW,EAAE;QAC5B,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,GAAW,EACX,QAAyB,EACzB,QAAsB,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC;IAEvE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACtC,IAAI,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO;IAEjC,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,CAAC;QACd,aAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
declare function resetCapWarning(): void;
|
|
2
|
+
interface QueueInstall {
|
|
3
|
+
installId: string;
|
|
4
|
+
mmagentVersion: string;
|
|
5
|
+
os: string;
|
|
6
|
+
nodeMajor: string;
|
|
7
|
+
language: string;
|
|
8
|
+
tzOffsetBucket: string;
|
|
9
|
+
}
|
|
10
|
+
export interface QueueRecord {
|
|
11
|
+
schemaVersion: number;
|
|
12
|
+
install: QueueInstall;
|
|
13
|
+
generation: number;
|
|
14
|
+
event: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface RecordMeta {
|
|
17
|
+
byteOffset: number;
|
|
18
|
+
byteLength: number;
|
|
19
|
+
sha256: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ReadBatchResult {
|
|
22
|
+
records: QueueRecord[];
|
|
23
|
+
meta: RecordMeta[];
|
|
24
|
+
}
|
|
25
|
+
export declare class Queue {
|
|
26
|
+
#private;
|
|
27
|
+
constructor(dir: string);
|
|
28
|
+
get queuePath(): string;
|
|
29
|
+
append(record: QueueRecord): Promise<void>;
|
|
30
|
+
readBatch(maxRecords?: number): Promise<ReadBatchResult>;
|
|
31
|
+
truncate(expectedMeta: RecordMeta[]): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export { resetCapWarning };
|
|
34
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/telemetry/queue.ts"],"names":[],"mappings":"AAkBA,iBAAS,eAAe,IAAI,IAAI,CAE/B;AAED,UAAU,YAAY;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,IAAI,EAAE,UAAU,EAAE,CAAC;CACpB;AAgCD,qBAAa,KAAK;;gBAMJ,GAAG,EAAE,MAAM;IAKvB,IAAI,SAAS,IAAI,MAAM,CAEtB;IAEK,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B1C,SAAS,CAAC,UAAU,SAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqDhE,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CA+G1D;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|