larkin 0.2.70
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/CONTRIBUTING.md +48 -0
- package/LICENSE +201 -0
- package/README.md +122 -0
- package/SECURITY.md +14 -0
- package/artifacts/release/THIRD_PARTY_NOTICES.txt +5488 -0
- package/dist/agent/agent-cli-capabilities.cjs +43 -0
- package/dist/agent/agent-cli-capabilities.mjs +35 -0
- package/dist/agent/agent-state-store.cjs +961 -0
- package/dist/agent/agent-state-store.mjs +923 -0
- package/dist/agent/agent-transport.cjs +201 -0
- package/dist/agent/agent-transport.mjs +194 -0
- package/dist/agent/config-cli-contract.cjs +19 -0
- package/dist/agent/config-cli-contract.mjs +16 -0
- package/dist/agent/context-prompt.cjs +159 -0
- package/dist/agent/context-prompt.mjs +155 -0
- package/dist/agent/freshness-gate.mjs +24 -0
- package/dist/agent/history-projection.cjs +72 -0
- package/dist/agent/history-projection.mjs +65 -0
- package/dist/agent/host-reminder-orchestrator.cjs +289 -0
- package/dist/agent/host-reminder-orchestrator.mjs +252 -0
- package/dist/agent/inbox-projection.cjs +92 -0
- package/dist/agent/inbox-projection.mjs +85 -0
- package/dist/agent/interaction-state-machine.cjs +710 -0
- package/dist/agent/interaction-state-machine.mjs +705 -0
- package/dist/agent/reminder-routes.cjs +192 -0
- package/dist/agent/reminder-routes.mjs +156 -0
- package/dist/agent/reminder-store.cjs +220 -0
- package/dist/agent/reminder-store.mjs +205 -0
- package/dist/agent/transport-business-context.cjs +486 -0
- package/dist/agent/transport-business-context.mjs +446 -0
- package/dist/agent/transport-shell.cjs +45 -0
- package/dist/agent/transport-shell.mjs +39 -0
- package/dist/app/agent-cli.mjs +596 -0
- package/dist/app/agent-config.mjs +633 -0
- package/dist/app/agent-lark-cli-workspace.cjs +95 -0
- package/dist/app/agent-lark-cli-workspace.mjs +85 -0
- package/dist/app/agent-readiness.cjs +42 -0
- package/dist/app/agent-readiness.mjs +38 -0
- package/dist/app/binary-entry.mjs +86 -0
- package/dist/app/cli.mjs +176 -0
- package/dist/app/dashboard.mjs +205 -0
- package/dist/app/internal-command.cjs +57 -0
- package/dist/app/internal-command.mjs +48 -0
- package/dist/app/lark-channel-secret.mjs +22 -0
- package/dist/app/lark-cli.mjs +887 -0
- package/dist/app/lark.mjs +45 -0
- package/dist/app/local-control.mjs +728 -0
- package/dist/app/official-lark-cli.cjs +138 -0
- package/dist/app/official-lark-cli.mjs +128 -0
- package/dist/app/pi-auth-cli.mjs +67 -0
- package/dist/app/run.mjs +364 -0
- package/dist/app/runtime-agent-config.mjs +416 -0
- package/dist/app/runtime-model-directory.mjs +78 -0
- package/dist/app/runtime-process.mjs +113 -0
- package/dist/app/session-cli.mjs +83 -0
- package/dist/app/setup-dashboard.mjs +26 -0
- package/dist/app/setup.mjs +166 -0
- package/dist/app/telemetry.mjs +83 -0
- package/dist/dashboard/dashboard-assets.mjs +21 -0
- package/dist/dashboard/dashboard-avatar.mjs +128 -0
- package/dist/dashboard/dashboard-config-controller.mjs +525 -0
- package/dist/dashboard/dashboard-lifecycle.cjs +97 -0
- package/dist/dashboard/dashboard-lifecycle.mjs +88 -0
- package/dist/dashboard/dashboard-template.mjs +29 -0
- package/dist/dashboard/dashboard-view-model.mjs +556 -0
- package/dist/dashboard/dashboard-workspace.mjs +104 -0
- package/dist/dashboard/web/assets/dashboard.css +3 -0
- package/dist/dashboard/web/assets/dashboard.js +49 -0
- package/dist/feishu/channel-transport.cjs +151 -0
- package/dist/feishu/channel-transport.mjs +148 -0
- package/dist/feishu/document-comment.cjs +120 -0
- package/dist/feishu/document-comment.mjs +109 -0
- package/dist/feishu/host-business-state.cjs +477 -0
- package/dist/feishu/host-business-state.mjs +462 -0
- package/dist/feishu/host-channel-business.cjs +161 -0
- package/dist/feishu/host-channel-business.mjs +157 -0
- package/dist/feishu/host-processing-eye.cjs +185 -0
- package/dist/feishu/host-processing-eye.mjs +181 -0
- package/dist/feishu/host-shell.cjs +1425 -0
- package/dist/feishu/host-shell.mjs +1385 -0
- package/dist/feishu/im-freshness-adapter.mjs +89 -0
- package/dist/feishu/interaction-orchestrator.cjs +279 -0
- package/dist/feishu/interaction-orchestrator.mjs +242 -0
- package/dist/feishu/lark-passthrough.mjs +70 -0
- package/dist/feishu/message-policy.cjs +111 -0
- package/dist/feishu/message-policy.mjs +98 -0
- package/dist/feishu/outbound-transport.cjs +221 -0
- package/dist/feishu/outbound-transport.mjs +215 -0
- package/dist/platform/build-info.cjs +25 -0
- package/dist/platform/build-info.mjs +18 -0
- package/dist/platform/callback-capability.cjs +301 -0
- package/dist/platform/callback-capability.mjs +252 -0
- package/dist/platform/check-bun-version.cjs +12 -0
- package/dist/platform/config.cjs +751 -0
- package/dist/platform/config.mjs +694 -0
- package/dist/platform/process-inspect.cjs +89 -0
- package/dist/platform/process-state.cjs +237 -0
- package/dist/platform/process-state.mjs +223 -0
- package/dist/platform/release-artifacts.mjs +61 -0
- package/dist/platform/root-layout.cjs +111 -0
- package/dist/platform/root-layout.mjs +72 -0
- package/dist/platform/telemetry-config.cjs +64 -0
- package/dist/platform/telemetry-config.mjs +56 -0
- package/dist/platform/telemetry-spool.cjs +661 -0
- package/dist/platform/telemetry-spool.mjs +653 -0
- package/dist/platform/telemetry-tracing.cjs +602 -0
- package/dist/platform/telemetry-tracing.mjs +596 -0
- package/dist/platform/telemetry-uploader.cjs +150 -0
- package/dist/platform/telemetry-uploader.mjs +146 -0
- package/dist/platform/workspace-service.cjs +469 -0
- package/dist/platform/workspace-service.mjs +430 -0
- package/dist/runtime/builtin-pi-assets.mjs +33 -0
- package/dist/runtime/claude-model-catalog.cjs +102 -0
- package/dist/runtime/claude-model-catalog.mjs +99 -0
- package/dist/runtime/codex-model-catalog.cjs +108 -0
- package/dist/runtime/codex-model-catalog.mjs +105 -0
- package/dist/runtime/pi-bash-timeout-extension.mjs +60 -0
- package/dist/runtime/pi-bash-timeout-injection.mjs +60 -0
- package/dist/runtime/pi-bash-timeout.bundle.js +43 -0
- package/dist/runtime/pi-model-catalog.cjs +116 -0
- package/dist/runtime/pi-model-catalog.mjs +75 -0
- package/dist/runtime/pi-official-auth.mjs +480 -0
- package/dist/runtime/pi-provider-config.cjs +252 -0
- package/dist/runtime/pi-provider-config.mjs +239 -0
- package/dist/runtime/pi-rpc-client.cjs +255 -0
- package/dist/runtime/pi-rpc-client.mjs +251 -0
- package/dist/runtime/pi-subagent-injection.mjs +129 -0
- package/dist/runtime/pi-subagents.bundle.js +8990 -0
- package/dist/runtime/runtime-adapters.mjs +1019 -0
- package/dist/runtime/runtime-contracts.cjs +2 -0
- package/dist/runtime/runtime-contracts.mjs +1 -0
- package/dist/runtime/runtime-host.cjs +930 -0
- package/dist/runtime/runtime-host.mjs +922 -0
- package/dist/runtime/runtime-model-catalog.cjs +43 -0
- package/dist/runtime/runtime-model-catalog.mjs +40 -0
- package/dist/runtime/runtime-readiness.cjs +177 -0
- package/dist/runtime/runtime-readiness.mjs +133 -0
- package/dist/setup/bot-register.mjs +655 -0
- package/dist/setup/grant-scopes.mjs +133 -0
- package/dist/setup/run-credential-preflight.mjs +89 -0
- package/dist/setup/setup-agent-choice.mjs +172 -0
- package/dist/setup/setup-bind.mjs +337 -0
- package/dist/setup/setup-binding.mjs +52 -0
- package/package.json +112 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.flushTelemetry = flushTelemetry;
|
|
4
|
+
exports.startTelemetryUploader = startTelemetryUploader;
|
|
5
|
+
class ProtocolResponseError extends Error {
|
|
6
|
+
}
|
|
7
|
+
async function boundedResponseJson(response, maxBytes = 64 * 1024) {
|
|
8
|
+
const declared = Number(response.headers.get("content-length"));
|
|
9
|
+
if (Number.isFinite(declared) && declared > maxBytes)
|
|
10
|
+
throw new ProtocolResponseError("OTLP response too large");
|
|
11
|
+
if (!response.body)
|
|
12
|
+
return null;
|
|
13
|
+
const reader = response.body.getReader();
|
|
14
|
+
const chunks = [];
|
|
15
|
+
let size = 0;
|
|
16
|
+
try {
|
|
17
|
+
while (true) {
|
|
18
|
+
const result = await reader.read();
|
|
19
|
+
if (result.done)
|
|
20
|
+
break;
|
|
21
|
+
size += result.value.byteLength;
|
|
22
|
+
if (size > maxBytes) {
|
|
23
|
+
await reader.cancel();
|
|
24
|
+
throw new ProtocolResponseError("OTLP response too large");
|
|
25
|
+
}
|
|
26
|
+
chunks.push(result.value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
reader.releaseLock();
|
|
31
|
+
}
|
|
32
|
+
if (!size)
|
|
33
|
+
return null;
|
|
34
|
+
const text = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString("utf8");
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(text);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new ProtocolResponseError("invalid OTLP response");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function flushTelemetry(spool, options) {
|
|
43
|
+
let release = null;
|
|
44
|
+
try {
|
|
45
|
+
release = spool.acquireLease();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "spool" };
|
|
49
|
+
}
|
|
50
|
+
if (!release)
|
|
51
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "busy" };
|
|
52
|
+
const safeDiagnostic = (lastErrorCategory, lastUploadAt) => {
|
|
53
|
+
try {
|
|
54
|
+
spool.updateDiagnostics({ lastErrorCategory, ...(lastUploadAt ? { lastUploadAt } : {}) });
|
|
55
|
+
}
|
|
56
|
+
catch { /* isolated */ }
|
|
57
|
+
};
|
|
58
|
+
let records;
|
|
59
|
+
try {
|
|
60
|
+
records = spool.list(options.batchFiles ?? 128, true);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
safeDiagnostic("spool");
|
|
64
|
+
release();
|
|
65
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "spool" };
|
|
66
|
+
}
|
|
67
|
+
if (!records.length) {
|
|
68
|
+
release();
|
|
69
|
+
return { uploadedFiles: 0, status: "empty" };
|
|
70
|
+
}
|
|
71
|
+
const payload = { resourceSpans: records.flatMap((record) => record.payload.resourceSpans) };
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);
|
|
74
|
+
timer.unref?.();
|
|
75
|
+
try {
|
|
76
|
+
const response = await (options.fetchImpl ?? fetch)(options.endpoint, {
|
|
77
|
+
method: "POST", redirect: "error", signal: controller.signal,
|
|
78
|
+
headers: { "content-type": "application/json", ...(options.headers ?? {}) },
|
|
79
|
+
body: JSON.stringify(payload),
|
|
80
|
+
});
|
|
81
|
+
if (response.status !== 200) {
|
|
82
|
+
const category = response.status === 429 ? "rate_limit" : response.status >= 500 ? "server" : response.ok ? "protocol" : "configuration";
|
|
83
|
+
safeDiagnostic(category);
|
|
84
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: category };
|
|
85
|
+
}
|
|
86
|
+
const body = await boundedResponseJson(response);
|
|
87
|
+
if (body !== null && (typeof body !== "object" || Array.isArray(body)))
|
|
88
|
+
throw new ProtocolResponseError("invalid OTLP response");
|
|
89
|
+
const partial = body?.partialSuccess;
|
|
90
|
+
if (partial !== undefined && (!partial || typeof partial !== "object" || Array.isArray(partial)))
|
|
91
|
+
throw new ProtocolResponseError("invalid OTLP partial success");
|
|
92
|
+
const partialValue = partial;
|
|
93
|
+
const rejected = Number(partialValue?.rejectedSpans ?? 0);
|
|
94
|
+
const errorMessage = partialValue?.errorMessage ?? "";
|
|
95
|
+
if (!Number.isSafeInteger(rejected) || rejected < 0 || typeof errorMessage !== "string")
|
|
96
|
+
throw new ProtocolResponseError("invalid OTLP partial success");
|
|
97
|
+
if (partialValue && rejected > 0) {
|
|
98
|
+
const spanCount = payload.resourceSpans.flatMap((resource) => resource.scopeSpans ?? [])
|
|
99
|
+
.reduce((sum, scope) => sum + (scope.spans?.length ?? 0), 0);
|
|
100
|
+
const droppedSpans = Math.max(1, Math.min(spanCount, rejected || 1));
|
|
101
|
+
spool.acknowledge(records);
|
|
102
|
+
try {
|
|
103
|
+
spool.updateDiagnostics({ droppedSpans: spool.status().droppedSpans + droppedSpans,
|
|
104
|
+
lastUploadAt: new Date().toISOString(), lastErrorCategory: "partial_success" });
|
|
105
|
+
}
|
|
106
|
+
catch { /* isolated */ }
|
|
107
|
+
return { uploadedFiles: 0, status: "dropped", errorCategory: "partial_success", droppedSpans };
|
|
108
|
+
}
|
|
109
|
+
spool.acknowledge(records);
|
|
110
|
+
safeDiagnostic(null, new Date().toISOString());
|
|
111
|
+
return { uploadedFiles: records.length, status: "uploaded" };
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const category = error.name === "AbortError" ? "timeout" : error instanceof ProtocolResponseError ? "protocol" : error instanceof TypeError ? "network" : "spool";
|
|
115
|
+
safeDiagnostic(category);
|
|
116
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: category };
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
release();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function startTelemetryUploader(spool, config) {
|
|
124
|
+
if (!config.endpoint)
|
|
125
|
+
return null;
|
|
126
|
+
let stopped = false;
|
|
127
|
+
let timer = null;
|
|
128
|
+
let failures = 0;
|
|
129
|
+
let active = null;
|
|
130
|
+
const flush = () => {
|
|
131
|
+
if (active)
|
|
132
|
+
return active;
|
|
133
|
+
active = flushTelemetry(spool, { endpoint: config.endpoint, headers: config.headers, timeoutMs: config.requestTimeoutMs })
|
|
134
|
+
.then((result) => { failures = result.status === "retained" ? failures + 1 : 0; return result; })
|
|
135
|
+
.catch(() => ({ uploadedFiles: 0, status: "retained", errorCategory: "internal" }))
|
|
136
|
+
.finally(() => { active = null; schedule(); });
|
|
137
|
+
return active;
|
|
138
|
+
};
|
|
139
|
+
const schedule = () => {
|
|
140
|
+
if (stopped || timer)
|
|
141
|
+
return;
|
|
142
|
+
const exponential = Math.min(6, failures);
|
|
143
|
+
const jitter = 0.8 + Math.random() * 0.4;
|
|
144
|
+
timer = setTimeout(() => { timer = null; void flush().catch(() => { }); }, Math.round(config.uploadIntervalMs * 2 ** exponential * jitter));
|
|
145
|
+
timer.unref?.();
|
|
146
|
+
};
|
|
147
|
+
schedule();
|
|
148
|
+
return { stop() { stopped = true; if (timer)
|
|
149
|
+
clearTimeout(timer); timer = null; }, flush };
|
|
150
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
class ProtocolResponseError extends Error {
|
|
2
|
+
}
|
|
3
|
+
async function boundedResponseJson(response, maxBytes = 64 * 1024) {
|
|
4
|
+
const declared = Number(response.headers.get("content-length"));
|
|
5
|
+
if (Number.isFinite(declared) && declared > maxBytes)
|
|
6
|
+
throw new ProtocolResponseError("OTLP response too large");
|
|
7
|
+
if (!response.body)
|
|
8
|
+
return null;
|
|
9
|
+
const reader = response.body.getReader();
|
|
10
|
+
const chunks = [];
|
|
11
|
+
let size = 0;
|
|
12
|
+
try {
|
|
13
|
+
while (true) {
|
|
14
|
+
const result = await reader.read();
|
|
15
|
+
if (result.done)
|
|
16
|
+
break;
|
|
17
|
+
size += result.value.byteLength;
|
|
18
|
+
if (size > maxBytes) {
|
|
19
|
+
await reader.cancel();
|
|
20
|
+
throw new ProtocolResponseError("OTLP response too large");
|
|
21
|
+
}
|
|
22
|
+
chunks.push(result.value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
reader.releaseLock();
|
|
27
|
+
}
|
|
28
|
+
if (!size)
|
|
29
|
+
return null;
|
|
30
|
+
const text = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString("utf8");
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(text);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new ProtocolResponseError("invalid OTLP response");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function flushTelemetry(spool, options) {
|
|
39
|
+
let release = null;
|
|
40
|
+
try {
|
|
41
|
+
release = spool.acquireLease();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "spool" };
|
|
45
|
+
}
|
|
46
|
+
if (!release)
|
|
47
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "busy" };
|
|
48
|
+
const safeDiagnostic = (lastErrorCategory, lastUploadAt) => {
|
|
49
|
+
try {
|
|
50
|
+
spool.updateDiagnostics({ lastErrorCategory, ...(lastUploadAt ? { lastUploadAt } : {}) });
|
|
51
|
+
}
|
|
52
|
+
catch { /* isolated */ }
|
|
53
|
+
};
|
|
54
|
+
let records;
|
|
55
|
+
try {
|
|
56
|
+
records = spool.list(options.batchFiles ?? 128, true);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
safeDiagnostic("spool");
|
|
60
|
+
release();
|
|
61
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: "spool" };
|
|
62
|
+
}
|
|
63
|
+
if (!records.length) {
|
|
64
|
+
release();
|
|
65
|
+
return { uploadedFiles: 0, status: "empty" };
|
|
66
|
+
}
|
|
67
|
+
const payload = { resourceSpans: records.flatMap((record) => record.payload.resourceSpans) };
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? 10_000);
|
|
70
|
+
timer.unref?.();
|
|
71
|
+
try {
|
|
72
|
+
const response = await (options.fetchImpl ?? fetch)(options.endpoint, {
|
|
73
|
+
method: "POST", redirect: "error", signal: controller.signal,
|
|
74
|
+
headers: { "content-type": "application/json", ...(options.headers ?? {}) },
|
|
75
|
+
body: JSON.stringify(payload),
|
|
76
|
+
});
|
|
77
|
+
if (response.status !== 200) {
|
|
78
|
+
const category = response.status === 429 ? "rate_limit" : response.status >= 500 ? "server" : response.ok ? "protocol" : "configuration";
|
|
79
|
+
safeDiagnostic(category);
|
|
80
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: category };
|
|
81
|
+
}
|
|
82
|
+
const body = await boundedResponseJson(response);
|
|
83
|
+
if (body !== null && (typeof body !== "object" || Array.isArray(body)))
|
|
84
|
+
throw new ProtocolResponseError("invalid OTLP response");
|
|
85
|
+
const partial = body?.partialSuccess;
|
|
86
|
+
if (partial !== undefined && (!partial || typeof partial !== "object" || Array.isArray(partial)))
|
|
87
|
+
throw new ProtocolResponseError("invalid OTLP partial success");
|
|
88
|
+
const partialValue = partial;
|
|
89
|
+
const rejected = Number(partialValue?.rejectedSpans ?? 0);
|
|
90
|
+
const errorMessage = partialValue?.errorMessage ?? "";
|
|
91
|
+
if (!Number.isSafeInteger(rejected) || rejected < 0 || typeof errorMessage !== "string")
|
|
92
|
+
throw new ProtocolResponseError("invalid OTLP partial success");
|
|
93
|
+
if (partialValue && rejected > 0) {
|
|
94
|
+
const spanCount = payload.resourceSpans.flatMap((resource) => resource.scopeSpans ?? [])
|
|
95
|
+
.reduce((sum, scope) => sum + (scope.spans?.length ?? 0), 0);
|
|
96
|
+
const droppedSpans = Math.max(1, Math.min(spanCount, rejected || 1));
|
|
97
|
+
spool.acknowledge(records);
|
|
98
|
+
try {
|
|
99
|
+
spool.updateDiagnostics({ droppedSpans: spool.status().droppedSpans + droppedSpans,
|
|
100
|
+
lastUploadAt: new Date().toISOString(), lastErrorCategory: "partial_success" });
|
|
101
|
+
}
|
|
102
|
+
catch { /* isolated */ }
|
|
103
|
+
return { uploadedFiles: 0, status: "dropped", errorCategory: "partial_success", droppedSpans };
|
|
104
|
+
}
|
|
105
|
+
spool.acknowledge(records);
|
|
106
|
+
safeDiagnostic(null, new Date().toISOString());
|
|
107
|
+
return { uploadedFiles: records.length, status: "uploaded" };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const category = error.name === "AbortError" ? "timeout" : error instanceof ProtocolResponseError ? "protocol" : error instanceof TypeError ? "network" : "spool";
|
|
111
|
+
safeDiagnostic(category);
|
|
112
|
+
return { uploadedFiles: 0, status: "retained", errorCategory: category };
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
release();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export function startTelemetryUploader(spool, config) {
|
|
120
|
+
if (!config.endpoint)
|
|
121
|
+
return null;
|
|
122
|
+
let stopped = false;
|
|
123
|
+
let timer = null;
|
|
124
|
+
let failures = 0;
|
|
125
|
+
let active = null;
|
|
126
|
+
const flush = () => {
|
|
127
|
+
if (active)
|
|
128
|
+
return active;
|
|
129
|
+
active = flushTelemetry(spool, { endpoint: config.endpoint, headers: config.headers, timeoutMs: config.requestTimeoutMs })
|
|
130
|
+
.then((result) => { failures = result.status === "retained" ? failures + 1 : 0; return result; })
|
|
131
|
+
.catch(() => ({ uploadedFiles: 0, status: "retained", errorCategory: "internal" }))
|
|
132
|
+
.finally(() => { active = null; schedule(); });
|
|
133
|
+
return active;
|
|
134
|
+
};
|
|
135
|
+
const schedule = () => {
|
|
136
|
+
if (stopped || timer)
|
|
137
|
+
return;
|
|
138
|
+
const exponential = Math.min(6, failures);
|
|
139
|
+
const jitter = 0.8 + Math.random() * 0.4;
|
|
140
|
+
timer = setTimeout(() => { timer = null; void flush().catch(() => { }); }, Math.round(config.uploadIntervalMs * 2 ** exponential * jitter));
|
|
141
|
+
timer.unref?.();
|
|
142
|
+
};
|
|
143
|
+
schedule();
|
|
144
|
+
return { stop() { stopped = true; if (timer)
|
|
145
|
+
clearTimeout(timer); timer = null; }, flush };
|
|
146
|
+
}
|