@clinebot/core 0.0.28 → 0.0.30
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 +7 -0
- package/dist/ClineCore.d.ts +28 -2
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/cline-account-service.d.ts +1 -1
- package/dist/account/cline-account-service.d.ts.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/types.d.ts +5 -0
- package/dist/account/types.d.ts.map +1 -1
- package/dist/auth/bounded-ttl-cache.d.ts +14 -0
- package/dist/auth/bounded-ttl-cache.d.ts.map +1 -0
- package/dist/auth/cline.d.ts +27 -2
- package/dist/auth/cline.d.ts.map +1 -1
- package/dist/auth/oca.d.ts.map +1 -1
- package/dist/chat/chat-schema.d.ts +11 -11
- package/dist/extensions/config/agent-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/agent-config-parser.d.ts +2 -2
- package/dist/extensions/config/agent-config-parser.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/hooks-config-loader.d.ts +1 -1
- package/dist/extensions/config/hooks-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/index.d.ts +2 -4
- package/dist/extensions/config/index.d.ts.map +1 -0
- package/dist/{runtime/commands.d.ts → extensions/config/runtime-commands.d.ts} +2 -3
- package/dist/extensions/config/runtime-commands.d.ts.map +1 -0
- package/dist/extensions/config/unified-config-file-watcher.d.ts.map +1 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -0
- package/dist/extensions/context/agentic-compaction.d.ts +13 -0
- package/dist/extensions/context/agentic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/basic-compaction.d.ts +9 -0
- package/dist/extensions/context/basic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/compaction-shared.d.ts +60 -0
- package/dist/extensions/context/compaction-shared.d.ts.map +1 -0
- package/dist/extensions/context/compaction.d.ts +20 -0
- package/dist/extensions/context/compaction.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client.d.ts +3 -0
- package/dist/extensions/mcp/client.d.ts.map +1 -0
- package/dist/extensions/mcp/config-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/index.d.ts +9 -0
- package/dist/extensions/mcp/index.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/manager.d.ts +1 -2
- package/dist/extensions/mcp/manager.d.ts.map +1 -0
- package/dist/extensions/mcp/name-transform.d.ts +3 -0
- package/dist/extensions/mcp/name-transform.d.ts.map +1 -0
- package/dist/extensions/mcp/policies.d.ts +15 -0
- package/dist/extensions/mcp/policies.d.ts.map +1 -0
- package/dist/extensions/mcp/tools.d.ts +4 -0
- package/dist/extensions/mcp/tools.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/types.d.ts +29 -1
- package/dist/extensions/mcp/types.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-config-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts +5 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-sandbox.d.ts +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -0
- package/dist/extensions/plugin-sandbox-bootstrap.js +485 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/persistent.d.ts +64 -0
- package/dist/hooks/persistent.d.ts.map +1 -0
- package/dist/hooks/subprocess-runner.d.ts +22 -0
- package/dist/hooks/subprocess-runner.d.ts.map +1 -0
- package/dist/hooks/subprocess.d.ts +189 -0
- package/dist/hooks/subprocess.d.ts.map +1 -0
- package/dist/index.d.ts +22 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +560 -447
- package/dist/prompt/default-system.d.ts +2 -0
- package/dist/prompt/default-system.d.ts.map +1 -0
- package/dist/providers/local-provider-service.d.ts +1 -1
- package/dist/providers/local-provider-service.d.ts.map +1 -1
- package/dist/runtime/checkpoint-hooks.d.ts +21 -0
- package/dist/runtime/checkpoint-hooks.d.ts.map +1 -0
- package/dist/runtime/hook-file-hooks.d.ts +1 -1
- package/dist/runtime/hook-file-hooks.d.ts.map +1 -1
- package/dist/runtime/rules.d.ts +1 -1
- package/dist/runtime/rules.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/session-runtime.d.ts +25 -5
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/subprocess-sandbox.d.ts.map +1 -0
- package/dist/runtime/team-runtime-registry.d.ts +1 -1
- package/dist/runtime/team-runtime-registry.d.ts.map +1 -1
- package/dist/runtime/tool-approval.d.ts +1 -1
- package/dist/session/default-session-manager.d.ts +9 -3
- package/dist/session/default-session-manager.d.ts.map +1 -1
- package/dist/session/file-session-service.d.ts +1 -1
- package/dist/session/file-session-service.d.ts.map +1 -1
- package/dist/session/{unified-session-persistence-service.d.ts → persistence-service.d.ts} +11 -42
- package/dist/session/persistence-service.d.ts.map +1 -0
- package/dist/session/rpc-session-service.d.ts +1 -1
- package/dist/session/rpc-session-service.d.ts.map +1 -1
- package/dist/session/session-agent-events.d.ts +1 -1
- package/dist/session/session-artifacts.d.ts.map +1 -1
- package/dist/session/session-config-builder.d.ts.map +1 -1
- package/dist/session/session-graph.d.ts +1 -1
- package/dist/session/session-graph.d.ts.map +1 -1
- package/dist/session/session-host.d.ts.map +1 -1
- package/dist/session/session-manager.d.ts +6 -5
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manifest.d.ts +1 -1
- package/dist/session/session-service.d.ts +3 -2
- package/dist/session/session-service.d.ts.map +1 -1
- package/dist/session/session-team-coordination.d.ts +2 -1
- package/dist/session/session-team-coordination.d.ts.map +1 -1
- package/dist/session/utils/helpers.d.ts +51 -3
- package/dist/session/utils/helpers.d.ts.map +1 -1
- package/dist/session/utils/types.d.ts +41 -7
- package/dist/session/utils/types.d.ts.map +1 -1
- package/dist/session/workspace-manager.d.ts +1 -2
- package/dist/session/workspace-manager.d.ts.map +1 -1
- package/dist/session/workspace-manifest.d.ts +1 -22
- package/dist/session/workspace-manifest.d.ts.map +1 -1
- package/dist/storage/file-team-store.d.ts +2 -1
- package/dist/storage/file-team-store.d.ts.map +1 -1
- package/dist/storage/sqlite-team-store.d.ts +4 -1
- package/dist/storage/sqlite-team-store.d.ts.map +1 -1
- package/dist/storage/team-store.d.ts.map +1 -1
- package/dist/team/delegated-agent.d.ts +44 -0
- package/dist/team/delegated-agent.d.ts.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/multi-agent.d.ts +229 -0
- package/dist/team/multi-agent.d.ts.map +1 -0
- package/dist/team/projections.d.ts +2 -2
- package/dist/team/projections.d.ts.map +1 -1
- package/dist/team/runtime.d.ts +5 -0
- package/dist/team/runtime.d.ts.map +1 -0
- package/dist/team/spawn-agent-tool.d.ts +85 -0
- package/dist/team/spawn-agent-tool.d.ts.map +1 -0
- package/dist/team/subagent-prompts.d.ts +4 -0
- package/dist/team/subagent-prompts.d.ts.map +1 -0
- package/dist/team/team-tools.d.ts +35 -0
- package/dist/team/team-tools.d.ts.map +1 -0
- package/dist/telemetry/OpenTelemetryProvider.d.ts +11 -1
- package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
- package/dist/telemetry/{LoggerTelemetryAdapter.d.ts → TelemetryLoggerSink.d.ts} +10 -4
- package/dist/telemetry/TelemetryLoggerSink.d.ts.map +1 -0
- package/dist/telemetry/TelemetryService.d.ts.map +1 -1
- package/dist/telemetry/index.js +15 -28
- package/dist/tools/definitions.d.ts +4 -3
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/index.d.ts +5 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/model-tool-routing.d.ts.map +1 -1
- package/dist/tools/presets.d.ts +26 -0
- package/dist/tools/presets.d.ts.map +1 -1
- package/dist/tools/schemas.d.ts +8 -0
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/types.d.ts +23 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/config.d.ts +47 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/provider-settings.d.ts +1 -1
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/storage.d.ts +2 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types.d.ts +7 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -12
- package/src/ClineCore.test.ts +150 -0
- package/src/ClineCore.ts +114 -8
- package/src/account/cline-account-service.test.ts +84 -0
- package/src/account/cline-account-service.ts +2 -2
- package/src/account/index.ts +1 -0
- package/src/account/types.ts +6 -0
- package/src/auth/bounded-ttl-cache.test.ts +38 -0
- package/src/auth/bounded-ttl-cache.ts +53 -0
- package/src/auth/cline.test.ts +173 -36
- package/src/auth/cline.ts +395 -93
- package/src/auth/oca.test.ts +125 -0
- package/src/auth/oca.ts +17 -4
- package/src/{agents → extensions/config}/agent-config-loader.test.ts +1 -1
- package/src/{agents → extensions/config}/agent-config-parser.ts +2 -2
- package/src/{agents → extensions/config}/hooks-config-loader.ts +1 -1
- package/src/{agents → extensions/config}/index.ts +7 -11
- package/src/{runtime/commands.test.ts → extensions/config/runtime-commands.test.ts} +20 -3
- package/src/{runtime/commands.ts → extensions/config/runtime-commands.ts} +1 -8
- package/src/{agents → extensions/config}/unified-config-file-watcher.ts +15 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.test.ts +90 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.ts +126 -12
- package/src/extensions/context/agentic-compaction.ts +119 -0
- package/src/extensions/context/basic-compaction.ts +275 -0
- package/src/extensions/context/compaction-shared.ts +458 -0
- package/src/extensions/context/compaction.test.ts +477 -0
- package/src/extensions/context/compaction.ts +203 -0
- package/src/extensions/index.ts +12 -0
- package/src/extensions/mcp/client.ts +420 -0
- package/src/{mcp → extensions/mcp}/index.ts +16 -0
- package/src/{mcp → extensions/mcp}/manager.test.ts +1 -2
- package/src/{mcp → extensions/mcp}/manager.ts +3 -5
- package/src/extensions/mcp/name-transform.ts +33 -0
- package/src/extensions/mcp/policies.ts +47 -0
- package/src/extensions/mcp/tools.ts +47 -0
- package/src/{mcp → extensions/mcp}/types.ts +35 -7
- package/src/{agents → extensions/plugin}/plugin-config-loader.test.ts +18 -13
- package/src/{agents → extensions/plugin}/plugin-config-loader.ts +1 -1
- package/src/{agents → extensions/plugin}/plugin-loader.test.ts +41 -4
- package/src/extensions/plugin/plugin-loader.ts +106 -0
- package/src/extensions/plugin/plugin-module-import.ts +278 -0
- package/src/{agents → extensions/plugin}/plugin-sandbox-bootstrap.ts +30 -92
- package/src/{agents → extensions/plugin}/plugin-sandbox.test.ts +60 -3
- package/src/{agents → extensions/plugin}/plugin-sandbox.ts +146 -56
- package/src/hooks/index.ts +25 -0
- package/src/hooks/persistent.ts +661 -0
- package/src/hooks/subprocess-runner.ts +196 -0
- package/src/hooks/subprocess.ts +669 -0
- package/src/index.ts +200 -118
- package/src/prompt/default-system.ts +21 -0
- package/src/providers/local-provider-registry.ts +1 -1
- package/src/providers/local-provider-service.test.ts +23 -2
- package/src/providers/local-provider-service.ts +2 -2
- package/src/runtime/checkpoint-hooks.test.ts +168 -0
- package/src/runtime/checkpoint-hooks.ts +186 -0
- package/src/runtime/hook-file-hooks.test.ts +40 -1
- package/src/runtime/hook-file-hooks.ts +35 -16
- package/src/runtime/index.ts +4 -19
- package/src/runtime/rules.ts +4 -1
- package/src/runtime/runtime-builder.team-persistence.test.ts +3 -6
- package/src/runtime/runtime-builder.test.ts +266 -160
- package/src/runtime/runtime-builder.ts +120 -47
- package/src/runtime/runtime-parity.test.ts +22 -22
- package/src/runtime/session-runtime.ts +36 -6
- package/src/runtime/{sandbox/subprocess-sandbox.ts → subprocess-sandbox.ts} +24 -3
- package/src/runtime/team-runtime-registry.ts +1 -4
- package/src/runtime/tool-approval.ts +1 -1
- package/src/session/default-session-manager.e2e.test.ts +2 -2
- package/src/session/default-session-manager.test.ts +553 -9
- package/src/session/default-session-manager.ts +162 -46
- package/src/session/file-session-service.ts +3 -3
- package/src/session/index.ts +6 -6
- package/src/session/persistence-service.test.ts +212 -0
- package/src/session/{unified-session-persistence-service.ts → persistence-service.ts} +106 -172
- package/src/session/rpc-session-service.ts +3 -3
- package/src/session/runtime-oauth-token-manager.ts +1 -1
- package/src/session/session-agent-events.ts +1 -1
- package/src/session/session-artifacts.ts +32 -4
- package/src/session/session-config-builder.ts +22 -9
- package/src/session/session-graph.ts +1 -1
- package/src/session/session-host.ts +19 -11
- package/src/session/session-manager.ts +11 -6
- package/src/session/session-service.team-persistence.test.ts +1 -1
- package/src/session/session-service.ts +6 -9
- package/src/session/session-team-coordination.ts +7 -3
- package/src/session/session-telemetry.ts +1 -1
- package/src/session/utils/helpers.test.ts +160 -0
- package/src/session/utils/helpers.ts +289 -42
- package/src/session/utils/types.ts +47 -7
- package/src/session/workspace-manager.ts +5 -3
- package/src/session/workspace-manifest.ts +3 -49
- package/src/storage/file-team-store.ts +2 -5
- package/src/storage/provider-settings-legacy-migration.ts +2 -2
- package/src/storage/provider-settings-manager.test.ts +1 -1
- package/src/storage/sqlite-team-store.ts +212 -125
- package/src/storage/team-store.ts +1 -5
- package/src/team/delegated-agent.ts +131 -0
- package/src/team/index.ts +1 -0
- package/src/team/multi-agent.lifecycle.test.ts +201 -0
- package/src/team/multi-agent.ts +1666 -0
- package/src/team/projections.ts +2 -4
- package/src/team/runtime.ts +54 -0
- package/src/team/spawn-agent-tool.test.ts +387 -0
- package/src/team/spawn-agent-tool.ts +207 -0
- package/src/team/subagent-prompts.ts +41 -0
- package/src/team/team-tools.test.ts +802 -0
- package/src/team/team-tools.ts +792 -0
- package/src/telemetry/OpenTelemetryAdapter.ts +1 -1
- package/src/telemetry/OpenTelemetryProvider.test.ts +216 -3
- package/src/telemetry/OpenTelemetryProvider.ts +110 -20
- package/src/telemetry/TelemetryLoggerSink.test.ts +42 -0
- package/src/telemetry/{LoggerTelemetryAdapter.ts → TelemetryLoggerSink.ts} +21 -14
- package/src/telemetry/TelemetryService.test.ts +7 -7
- package/src/telemetry/TelemetryService.ts +2 -4
- package/src/tools/definitions.test.ts +76 -0
- package/src/tools/definitions.ts +41 -2
- package/src/tools/executors/apply-patch.ts +1 -1
- package/src/tools/executors/editor.ts +1 -1
- package/src/tools/executors/file-read.ts +1 -1
- package/src/tools/executors/search.ts +1 -1
- package/src/tools/executors/web-fetch.ts +1 -1
- package/src/tools/index.ts +6 -1
- package/src/tools/model-tool-routing.ts +2 -0
- package/src/tools/presets.test.ts +8 -0
- package/src/tools/presets.ts +40 -2
- package/src/tools/schemas.ts +19 -0
- package/src/tools/types.ts +31 -2
- package/src/types/config.ts +61 -7
- package/src/types/events.ts +1 -1
- package/src/types/index.ts +0 -1
- package/src/types/provider-settings.ts +1 -1
- package/src/types/storage.ts +2 -5
- package/src/types.ts +32 -44
- package/dist/agents/agent-config-loader.d.ts.map +0 -1
- package/dist/agents/agent-config-parser.d.ts.map +0 -1
- package/dist/agents/hooks-config-loader.d.ts.map +0 -1
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/plugin-config-loader.d.ts.map +0 -1
- package/dist/agents/plugin-loader.d.ts.map +0 -1
- package/dist/agents/plugin-sandbox-bootstrap.js +0 -446
- package/dist/agents/plugin-sandbox.d.ts.map +0 -1
- package/dist/agents/unified-config-file-watcher.d.ts.map +0 -1
- package/dist/agents/user-instruction-config-loader.d.ts.map +0 -1
- package/dist/mcp/config-loader.d.ts.map +0 -1
- package/dist/mcp/index.d.ts +0 -5
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/manager.d.ts.map +0 -1
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/runtime/commands.d.ts.map +0 -1
- package/dist/runtime/sandbox/subprocess-sandbox.d.ts.map +0 -1
- package/dist/runtime/skills.d.ts +0 -14
- package/dist/runtime/skills.d.ts.map +0 -1
- package/dist/runtime/workflows.d.ts +0 -14
- package/dist/runtime/workflows.d.ts.map +0 -1
- package/dist/session/unified-session-persistence-service.d.ts.map +0 -1
- package/dist/telemetry/LoggerTelemetryAdapter.d.ts.map +0 -1
- package/dist/types/workspace.d.ts +0 -8
- package/dist/types/workspace.d.ts.map +0 -1
- package/src/agents/plugin-loader.ts +0 -175
- package/src/runtime/skills.ts +0 -44
- package/src/runtime/workflows.test.ts +0 -119
- package/src/runtime/workflows.ts +0 -45
- package/src/session/unified-session-persistence-service.test.ts +0 -85
- package/src/telemetry/LoggerTelemetryAdapter.test.ts +0 -42
- package/src/types/workspace.ts +0 -7
- /package/dist/{agents → extensions/config}/agent-config-loader.d.ts +0 -0
- /package/dist/{agents → extensions/config}/unified-config-file-watcher.d.ts +0 -0
- /package/dist/{agents → extensions/config}/user-instruction-config-loader.d.ts +0 -0
- /package/dist/{mcp → extensions/mcp}/config-loader.d.ts +0 -0
- /package/dist/runtime/{sandbox/subprocess-sandbox.d.ts → subprocess-sandbox.d.ts} +0 -0
- /package/src/{agents → extensions/config}/agent-config-loader.ts +0 -0
- /package/src/{agents → extensions/config}/hooks-config-loader.test.ts +0 -0
- /package/src/{agents → extensions/config}/unified-config-file-watcher.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.ts +0 -0
|
@@ -229,8 +229,8 @@ export class OpenTelemetryAdapter implements ITelemetryAdapter {
|
|
|
229
229
|
): TelemetryProperties {
|
|
230
230
|
return {
|
|
231
231
|
...this.commonProperties,
|
|
232
|
-
...properties,
|
|
233
232
|
...this.metadata,
|
|
233
|
+
...properties,
|
|
234
234
|
...(this.distinctId ? { distinct_id: this.distinctId } : {}),
|
|
235
235
|
...(required ? { _required: true } : {}),
|
|
236
236
|
};
|
|
@@ -52,6 +52,29 @@ describe("createOpenTelemetryTelemetryService", () => {
|
|
|
52
52
|
await provider.dispose();
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
it("registers a tracer provider when tracesExporter is set", async () => {
|
|
56
|
+
const { provider } = createOpenTelemetryTelemetryService({
|
|
57
|
+
metadata: {
|
|
58
|
+
extension_version: "1.2.3",
|
|
59
|
+
cline_type: "cli",
|
|
60
|
+
platform: "terminal",
|
|
61
|
+
platform_version: process.version,
|
|
62
|
+
os_type: process.platform,
|
|
63
|
+
os_version: "unknown",
|
|
64
|
+
},
|
|
65
|
+
enabled: true,
|
|
66
|
+
tracesExporter: "console",
|
|
67
|
+
logsExporter: "console",
|
|
68
|
+
metricsExporter: "console",
|
|
69
|
+
serviceName: "cline-test",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(provider.tracerProvider).not.toBeNull();
|
|
73
|
+
const span = provider.getTracer("test").startSpan("verify.tracing");
|
|
74
|
+
span.end();
|
|
75
|
+
await provider.dispose();
|
|
76
|
+
});
|
|
77
|
+
|
|
55
78
|
it("does not create an OTEL provider when disabled", () => {
|
|
56
79
|
const providerSpy = vi.spyOn(
|
|
57
80
|
OpenTelemetryProvider.prototype,
|
|
@@ -75,11 +98,201 @@ describe("createOpenTelemetryTelemetryService", () => {
|
|
|
75
98
|
expect(telemetry).toBeInstanceOf(TelemetryService);
|
|
76
99
|
});
|
|
77
100
|
|
|
101
|
+
it("preserves metadata when disabled", () => {
|
|
102
|
+
const metadata = {
|
|
103
|
+
extension_version: "1.0.0",
|
|
104
|
+
cline_type: "kanban",
|
|
105
|
+
platform: "kanban",
|
|
106
|
+
platform_version: "v22.0.0",
|
|
107
|
+
os_type: "darwin",
|
|
108
|
+
os_version: "15.0",
|
|
109
|
+
};
|
|
110
|
+
const { telemetry } = createConfiguredTelemetryService({
|
|
111
|
+
metadata,
|
|
112
|
+
enabled: false,
|
|
113
|
+
});
|
|
114
|
+
const spy = vi.fn();
|
|
115
|
+
(telemetry as any).adapters.push({
|
|
116
|
+
name: "test",
|
|
117
|
+
emit: spy,
|
|
118
|
+
emitRequired: spy,
|
|
119
|
+
isEnabled: () => true,
|
|
120
|
+
recordCounter: vi.fn(),
|
|
121
|
+
recordHistogram: vi.fn(),
|
|
122
|
+
recordGauge: vi.fn(),
|
|
123
|
+
flush: async () => {},
|
|
124
|
+
dispose: async () => {},
|
|
125
|
+
});
|
|
126
|
+
telemetry.captureRequired("test.event", {});
|
|
127
|
+
expect(spy).toHaveBeenCalledWith(
|
|
128
|
+
"test.event",
|
|
129
|
+
expect.objectContaining({
|
|
130
|
+
cline_type: "kanban",
|
|
131
|
+
platform: "kanban",
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("preserves metadata in the enabled (OTEL) path", async () => {
|
|
137
|
+
const metadata = {
|
|
138
|
+
extension_version: "1.0.0",
|
|
139
|
+
cline_type: "kanban",
|
|
140
|
+
platform: "kanban",
|
|
141
|
+
platform_version: "v22.0.0",
|
|
142
|
+
os_type: "darwin",
|
|
143
|
+
os_version: "15.0",
|
|
144
|
+
};
|
|
145
|
+
const { telemetry, provider } = createOpenTelemetryTelemetryService({
|
|
146
|
+
metadata,
|
|
147
|
+
enabled: true,
|
|
148
|
+
logsExporter: "console",
|
|
149
|
+
});
|
|
150
|
+
const spy = vi.fn();
|
|
151
|
+
(telemetry as any).adapters.push({
|
|
152
|
+
name: "test",
|
|
153
|
+
emit: spy,
|
|
154
|
+
emitRequired: spy,
|
|
155
|
+
isEnabled: () => true,
|
|
156
|
+
recordCounter: vi.fn(),
|
|
157
|
+
recordHistogram: vi.fn(),
|
|
158
|
+
recordGauge: vi.fn(),
|
|
159
|
+
flush: async () => {},
|
|
160
|
+
dispose: async () => {},
|
|
161
|
+
});
|
|
162
|
+
telemetry.captureRequired("test.event", {});
|
|
163
|
+
expect(spy).toHaveBeenCalledWith(
|
|
164
|
+
"test.event",
|
|
165
|
+
expect.objectContaining({
|
|
166
|
+
cline_type: "kanban",
|
|
167
|
+
platform: "kanban",
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
170
|
+
await provider.dispose();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("delivers metadata to the OTEL logger without duplication", async () => {
|
|
174
|
+
const otelEmit = vi.fn();
|
|
175
|
+
const provider = new OpenTelemetryProvider({
|
|
176
|
+
enabled: true,
|
|
177
|
+
});
|
|
178
|
+
// Replace the loggerProvider with a mock so we can inspect emit calls
|
|
179
|
+
(provider as any).loggerProvider = {
|
|
180
|
+
getLogger: () => ({ emit: otelEmit }),
|
|
181
|
+
forceFlush: async () => {},
|
|
182
|
+
shutdown: async () => {},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const metadata = {
|
|
186
|
+
extension_version: "1.0.0",
|
|
187
|
+
cline_type: "kanban",
|
|
188
|
+
platform: "kanban",
|
|
189
|
+
platform_version: "v22.0.0",
|
|
190
|
+
os_type: "darwin",
|
|
191
|
+
os_version: "15.0",
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const telemetry = provider.createTelemetryService({ metadata });
|
|
195
|
+
|
|
196
|
+
telemetry.captureRequired("test.otel_event", { custom_prop: "value" });
|
|
197
|
+
|
|
198
|
+
expect(otelEmit).toHaveBeenCalledTimes(1);
|
|
199
|
+
const emittedAttributes = otelEmit.mock.calls[0][0].attributes;
|
|
200
|
+
|
|
201
|
+
// Metadata fields must be present
|
|
202
|
+
expect(emittedAttributes).toMatchObject({
|
|
203
|
+
cline_type: "kanban",
|
|
204
|
+
platform: "kanban",
|
|
205
|
+
extension_version: "1.0.0",
|
|
206
|
+
custom_prop: "value",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Verify no key appears more than once (flattened object can't have
|
|
210
|
+
// duplicate keys, but this guards against nested duplication patterns
|
|
211
|
+
// like metadata appearing under a sub-prefix)
|
|
212
|
+
const keys = Object.keys(emittedAttributes);
|
|
213
|
+
const metadataKeys = Object.keys(metadata);
|
|
214
|
+
for (const mk of metadataKeys) {
|
|
215
|
+
const occurrences = keys.filter((k) => k === mk || k.endsWith(`.${mk}`));
|
|
216
|
+
expect(
|
|
217
|
+
occurrences,
|
|
218
|
+
`metadata key "${mk}" should appear exactly once, found: ${occurrences.join(", ")}`,
|
|
219
|
+
).toHaveLength(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
await provider.dispose();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("propagates updateMetadata to OTEL logger output", async () => {
|
|
226
|
+
const otelEmit = vi.fn();
|
|
227
|
+
const provider = new OpenTelemetryProvider({
|
|
228
|
+
enabled: true,
|
|
229
|
+
});
|
|
230
|
+
(provider as any).loggerProvider = {
|
|
231
|
+
getLogger: () => ({ emit: otelEmit }),
|
|
232
|
+
forceFlush: async () => {},
|
|
233
|
+
shutdown: async () => {},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const metadata = {
|
|
237
|
+
extension_version: "1.0.0",
|
|
238
|
+
cline_type: "kanban",
|
|
239
|
+
platform: "kanban",
|
|
240
|
+
platform_version: "v22.0.0",
|
|
241
|
+
os_type: "darwin",
|
|
242
|
+
os_version: "15.0",
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const telemetry = provider.createTelemetryService({ metadata });
|
|
246
|
+
|
|
247
|
+
// Update metadata after construction
|
|
248
|
+
telemetry.updateMetadata({ cline_type: "kanban-updated" });
|
|
249
|
+
|
|
250
|
+
telemetry.captureRequired("test.updated_event", {});
|
|
251
|
+
|
|
252
|
+
// The OTEL logger should see the updated value
|
|
253
|
+
const emittedAttributes =
|
|
254
|
+
otelEmit.mock.calls[otelEmit.mock.calls.length - 1][0].attributes;
|
|
255
|
+
expect(emittedAttributes.cline_type).toBe("kanban-updated");
|
|
256
|
+
|
|
257
|
+
await provider.dispose();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("preserves logger when disabled", () => {
|
|
261
|
+
const logger: BasicLogger = {
|
|
262
|
+
debug: vi.fn(),
|
|
263
|
+
log: vi.fn(),
|
|
264
|
+
error: vi.fn(),
|
|
265
|
+
};
|
|
266
|
+
const { telemetry } = createConfiguredTelemetryService({
|
|
267
|
+
metadata: {
|
|
268
|
+
extension_version: "1.0.0",
|
|
269
|
+
cline_type: "kanban",
|
|
270
|
+
platform: "kanban",
|
|
271
|
+
platform_version: "v22.0.0",
|
|
272
|
+
os_type: "darwin",
|
|
273
|
+
os_version: "15.0",
|
|
274
|
+
},
|
|
275
|
+
enabled: false,
|
|
276
|
+
logger,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
telemetry.capture({
|
|
280
|
+
event: "session.started",
|
|
281
|
+
properties: { sessionId: "session-1" },
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
285
|
+
"telemetry.event",
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
event: "session.started",
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
78
292
|
it("attaches the logger adapter when creating configured telemetry", () => {
|
|
79
293
|
const logger: BasicLogger = {
|
|
80
294
|
debug: vi.fn(),
|
|
81
|
-
|
|
82
|
-
warn: vi.fn(),
|
|
295
|
+
log: vi.fn(),
|
|
83
296
|
error: vi.fn(),
|
|
84
297
|
};
|
|
85
298
|
const { telemetry, provider } = createConfiguredTelemetryService({
|
|
@@ -101,7 +314,7 @@ describe("createOpenTelemetryTelemetryService", () => {
|
|
|
101
314
|
properties: { sessionId: "session-1" },
|
|
102
315
|
});
|
|
103
316
|
|
|
104
|
-
expect(logger.
|
|
317
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
105
318
|
"telemetry.event",
|
|
106
319
|
expect.objectContaining({
|
|
107
320
|
event: "session.started",
|
|
@@ -4,11 +4,12 @@ import type {
|
|
|
4
4
|
OpenTelemetryClientConfig,
|
|
5
5
|
TelemetryMetadata,
|
|
6
6
|
} from "@clinebot/shared";
|
|
7
|
-
import { metrics } from "@opentelemetry/api";
|
|
7
|
+
import { metrics, type Tracer, trace } from "@opentelemetry/api";
|
|
8
8
|
import { logs } from "@opentelemetry/api-logs";
|
|
9
9
|
import { OTLPLogExporter as OTLPLogExporterHttp } from "@opentelemetry/exporter-logs-otlp-http";
|
|
10
10
|
import { OTLPMetricExporter as OTLPMetricExporterHttp } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
11
|
-
import {
|
|
11
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
12
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
12
13
|
import {
|
|
13
14
|
BatchLogRecordProcessor,
|
|
14
15
|
ConsoleLogRecordExporter,
|
|
@@ -21,6 +22,13 @@ import {
|
|
|
21
22
|
type MetricReader,
|
|
22
23
|
PeriodicExportingMetricReader,
|
|
23
24
|
} from "@opentelemetry/sdk-metrics";
|
|
25
|
+
import {
|
|
26
|
+
BatchSpanProcessor,
|
|
27
|
+
ConsoleSpanExporter,
|
|
28
|
+
SimpleSpanProcessor,
|
|
29
|
+
type SpanProcessor,
|
|
30
|
+
} from "@opentelemetry/sdk-trace-base";
|
|
31
|
+
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
24
32
|
import {
|
|
25
33
|
ATTR_SERVICE_NAME,
|
|
26
34
|
ATTR_SERVICE_VERSION,
|
|
@@ -38,13 +46,14 @@ type OpenTelemetryProtocol = "http/json";
|
|
|
38
46
|
export interface OpenTelemetryProviderOptions
|
|
39
47
|
extends Omit<
|
|
40
48
|
OpenTelemetryClientConfig,
|
|
41
|
-
"enabled" | "logsExporter" | "metricsExporter"
|
|
49
|
+
"enabled" | "logsExporter" | "metricsExporter" | "tracesExporter"
|
|
42
50
|
> {
|
|
43
51
|
serviceName?: string;
|
|
44
52
|
serviceVersion?: string;
|
|
45
53
|
enabled?: boolean;
|
|
46
54
|
logsExporter?: string | OpenTelemetryExporterKind[];
|
|
47
55
|
metricsExporter?: string | OpenTelemetryExporterKind[];
|
|
56
|
+
tracesExporter?: string | OpenTelemetryExporterKind[];
|
|
48
57
|
metricExportIntervalMs?: number;
|
|
49
58
|
logMaxQueueSize?: number;
|
|
50
59
|
logBatchSize?: number;
|
|
@@ -64,11 +73,12 @@ export interface CreateOpenTelemetryTelemetryServiceOptions
|
|
|
64
73
|
export class OpenTelemetryProvider {
|
|
65
74
|
readonly meterProvider: MeterProvider | null;
|
|
66
75
|
readonly loggerProvider: LoggerProvider | null;
|
|
76
|
+
readonly tracerProvider: NodeTracerProvider | null;
|
|
67
77
|
private readonly options: OpenTelemetryProviderOptions;
|
|
68
78
|
|
|
69
79
|
constructor(options: OpenTelemetryProviderOptions = {}) {
|
|
70
80
|
this.options = options;
|
|
71
|
-
const resource =
|
|
81
|
+
const resource = resourceFromAttributes({
|
|
72
82
|
[ATTR_SERVICE_NAME]: options.serviceName ?? "cline",
|
|
73
83
|
...(options.serviceVersion
|
|
74
84
|
? { [ATTR_SERVICE_VERSION]: options.serviceVersion }
|
|
@@ -77,6 +87,7 @@ export class OpenTelemetryProvider {
|
|
|
77
87
|
|
|
78
88
|
this.meterProvider = this.createMeterProvider(resource);
|
|
79
89
|
this.loggerProvider = this.createLoggerProvider(resource);
|
|
90
|
+
this.tracerProvider = this.createTracerProvider(resource);
|
|
80
91
|
|
|
81
92
|
if (this.meterProvider) {
|
|
82
93
|
metrics.setGlobalMeterProvider(this.meterProvider);
|
|
@@ -84,6 +95,17 @@ export class OpenTelemetryProvider {
|
|
|
84
95
|
if (this.loggerProvider) {
|
|
85
96
|
logs.setGlobalLoggerProvider(this.loggerProvider);
|
|
86
97
|
}
|
|
98
|
+
if (this.tracerProvider) {
|
|
99
|
+
this.tracerProvider.register();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns a tracer for manual spans. Requires {@link OpenTelemetryProviderOptions.tracesExporter}
|
|
105
|
+
* so that a {@link NodeTracerProvider} is registered.
|
|
106
|
+
*/
|
|
107
|
+
getTracer(name = "cline", version?: string): Tracer {
|
|
108
|
+
return trace.getTracer(name, version ?? this.options.serviceVersion);
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
createAdapter(
|
|
@@ -111,10 +133,9 @@ export class OpenTelemetryProvider {
|
|
|
111
133
|
metadata: options.metadata,
|
|
112
134
|
});
|
|
113
135
|
return new TelemetryService({
|
|
136
|
+
...options,
|
|
114
137
|
adapters: [adapter],
|
|
115
138
|
distinctId: resolveCoreDistinctId(options.distinctId),
|
|
116
|
-
commonProperties: options.commonProperties,
|
|
117
|
-
logger: options.logger,
|
|
118
139
|
});
|
|
119
140
|
}
|
|
120
141
|
|
|
@@ -122,6 +143,7 @@ export class OpenTelemetryProvider {
|
|
|
122
143
|
await Promise.all([
|
|
123
144
|
this.meterProvider?.forceFlush?.(),
|
|
124
145
|
this.loggerProvider?.forceFlush?.(),
|
|
146
|
+
this.tracerProvider?.forceFlush?.(),
|
|
125
147
|
]);
|
|
126
148
|
}
|
|
127
149
|
|
|
@@ -129,10 +151,13 @@ export class OpenTelemetryProvider {
|
|
|
129
151
|
await Promise.all([
|
|
130
152
|
this.meterProvider?.shutdown?.(),
|
|
131
153
|
this.loggerProvider?.shutdown?.(),
|
|
154
|
+
this.tracerProvider?.shutdown?.(),
|
|
132
155
|
]);
|
|
133
156
|
}
|
|
134
157
|
|
|
135
|
-
private createMeterProvider(
|
|
158
|
+
private createMeterProvider(
|
|
159
|
+
resource: ReturnType<typeof resourceFromAttributes>,
|
|
160
|
+
): MeterProvider | null {
|
|
136
161
|
const exporters = normalizeExporters(this.options.metricsExporter);
|
|
137
162
|
if (exporters.length === 0) {
|
|
138
163
|
return null;
|
|
@@ -168,35 +193,71 @@ export class OpenTelemetryProvider {
|
|
|
168
193
|
});
|
|
169
194
|
}
|
|
170
195
|
|
|
171
|
-
private
|
|
172
|
-
|
|
196
|
+
private createTracerProvider(
|
|
197
|
+
resource: ReturnType<typeof resourceFromAttributes>,
|
|
198
|
+
): NodeTracerProvider | null {
|
|
199
|
+
const exporters = normalizeExporters(this.options.tracesExporter);
|
|
173
200
|
if (exporters.length === 0) {
|
|
174
201
|
return null;
|
|
175
202
|
}
|
|
176
203
|
|
|
177
|
-
const
|
|
204
|
+
const traceEndpoint =
|
|
205
|
+
this.options.otlpTracesEndpoint ?? this.options.otlpEndpoint;
|
|
206
|
+
const traceHeaders =
|
|
207
|
+
this.options.otlpTracesHeaders ?? this.options.otlpHeaders;
|
|
208
|
+
|
|
209
|
+
const processors: SpanProcessor[] = [];
|
|
178
210
|
for (const exporter of exporters) {
|
|
179
|
-
const
|
|
180
|
-
endpoint:
|
|
181
|
-
headers:
|
|
211
|
+
const processor = createSpanProcessor(exporter, {
|
|
212
|
+
endpoint: traceEndpoint,
|
|
213
|
+
headers: traceHeaders,
|
|
182
214
|
insecure: this.options.otlpInsecure ?? false,
|
|
183
215
|
protocol: "http/json",
|
|
184
216
|
});
|
|
185
|
-
if (
|
|
186
|
-
|
|
217
|
+
if (processor) {
|
|
218
|
+
processors.push(processor);
|
|
187
219
|
}
|
|
188
|
-
|
|
189
|
-
|
|
220
|
+
}
|
|
221
|
+
if (processors.length === 0) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return new NodeTracerProvider({ resource, spanProcessors: processors });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private createLoggerProvider(
|
|
229
|
+
resource: ReturnType<typeof resourceFromAttributes>,
|
|
230
|
+
): LoggerProvider | null {
|
|
231
|
+
const exporters = normalizeExporters(this.options.logsExporter);
|
|
232
|
+
if (exporters.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const processors = exporters
|
|
237
|
+
.map((exporter) => {
|
|
238
|
+
const logExporter = createLogExporter(exporter, {
|
|
239
|
+
endpoint: this.options.otlpEndpoint,
|
|
240
|
+
headers: this.options.otlpHeaders,
|
|
241
|
+
insecure: this.options.otlpInsecure ?? false,
|
|
242
|
+
protocol: "http/json",
|
|
243
|
+
});
|
|
244
|
+
if (!logExporter) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
return new BatchLogRecordProcessor(logExporter, {
|
|
190
248
|
maxQueueSize: this.options.logMaxQueueSize ?? 2048,
|
|
191
249
|
maxExportBatchSize: this.options.logBatchSize ?? 512,
|
|
192
250
|
scheduledDelayMillis:
|
|
193
251
|
this.options.logBatchTimeoutMs ??
|
|
194
252
|
this.options.logBatchTimeout ??
|
|
195
253
|
5000,
|
|
196
|
-
})
|
|
197
|
-
)
|
|
254
|
+
});
|
|
255
|
+
})
|
|
256
|
+
.filter((p): p is BatchLogRecordProcessor => p !== null);
|
|
257
|
+
if (processors.length === 0) {
|
|
258
|
+
return null;
|
|
198
259
|
}
|
|
199
|
-
return
|
|
260
|
+
return new LoggerProvider({ resource, processors });
|
|
200
261
|
}
|
|
201
262
|
}
|
|
202
263
|
|
|
@@ -214,6 +275,9 @@ export function createOpenTelemetryTelemetryService(
|
|
|
214
275
|
metricsExporter: Array.isArray(options.metricsExporter)
|
|
215
276
|
? options.metricsExporter.join(",")
|
|
216
277
|
: options.metricsExporter,
|
|
278
|
+
tracesExporter: Array.isArray(options.tracesExporter)
|
|
279
|
+
? options.tracesExporter.join(",")
|
|
280
|
+
: options.tracesExporter,
|
|
217
281
|
otlpProtocol: options.otlpProtocol,
|
|
218
282
|
hasOtlpEndpoint: Boolean(options.otlpEndpoint),
|
|
219
283
|
serviceName: options.serviceName,
|
|
@@ -234,6 +298,7 @@ export function createConfiguredTelemetryService(
|
|
|
234
298
|
if (options.enabled !== true) {
|
|
235
299
|
return {
|
|
236
300
|
telemetry: new TelemetryService({
|
|
301
|
+
...options,
|
|
237
302
|
distinctId: resolveCoreDistinctId(options.distinctId),
|
|
238
303
|
}),
|
|
239
304
|
};
|
|
@@ -280,6 +345,31 @@ function createLogExporter(
|
|
|
280
345
|
});
|
|
281
346
|
}
|
|
282
347
|
|
|
348
|
+
function createSpanProcessor(
|
|
349
|
+
exporter: OpenTelemetryExporterKind,
|
|
350
|
+
options: {
|
|
351
|
+
endpoint?: string;
|
|
352
|
+
headers?: Record<string, string>;
|
|
353
|
+
insecure: boolean;
|
|
354
|
+
protocol: OpenTelemetryProtocol;
|
|
355
|
+
},
|
|
356
|
+
): SpanProcessor | null {
|
|
357
|
+
if (exporter === "console") {
|
|
358
|
+
return new SimpleSpanProcessor(new ConsoleSpanExporter());
|
|
359
|
+
}
|
|
360
|
+
if (!options.endpoint) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const endpoint = ensurePathSuffix(options.endpoint, "/v1/traces");
|
|
365
|
+
return new BatchSpanProcessor(
|
|
366
|
+
new OTLPTraceExporter({
|
|
367
|
+
url: endpoint,
|
|
368
|
+
headers: options.headers,
|
|
369
|
+
}),
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
283
373
|
function createMetricReader(
|
|
284
374
|
exporter: OpenTelemetryExporterKind,
|
|
285
375
|
options: {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { TelemetryLoggerSink } from "./TelemetryLoggerSink";
|
|
3
|
+
|
|
4
|
+
describe("TelemetryLoggerSink", () => {
|
|
5
|
+
it("logs events and metrics through the provided logger", async () => {
|
|
6
|
+
const logger = {
|
|
7
|
+
debug: vi.fn(),
|
|
8
|
+
log: vi.fn(),
|
|
9
|
+
};
|
|
10
|
+
const sink = new TelemetryLoggerSink({ logger });
|
|
11
|
+
|
|
12
|
+
sink.emit("session.started", { sessionId: "s1" });
|
|
13
|
+
sink.emitRequired("user.opt_out", { reason: "manual" });
|
|
14
|
+
sink.recordCounter("cline.session.starts.total", 1, {
|
|
15
|
+
sessionId: "s1",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(logger.log).toHaveBeenCalledWith("telemetry.event", {
|
|
19
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
20
|
+
event: "session.started",
|
|
21
|
+
properties: { sessionId: "s1" },
|
|
22
|
+
});
|
|
23
|
+
expect(logger.log).toHaveBeenCalledWith("telemetry.required_event", {
|
|
24
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
25
|
+
severity: "warn",
|
|
26
|
+
event: "user.opt_out",
|
|
27
|
+
properties: { reason: "manual" },
|
|
28
|
+
});
|
|
29
|
+
expect(logger.debug).toHaveBeenCalledWith("telemetry.metric", {
|
|
30
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
31
|
+
instrument: "counter",
|
|
32
|
+
name: "cline.session.starts.total",
|
|
33
|
+
value: 1,
|
|
34
|
+
attributes: { sessionId: "s1" },
|
|
35
|
+
description: undefined,
|
|
36
|
+
required: false,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await sink.flush();
|
|
40
|
+
await sink.dispose();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -4,20 +4,26 @@ import type {
|
|
|
4
4
|
TelemetryProperties,
|
|
5
5
|
} from "./ITelemetryAdapter";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* {@link ITelemetryAdapter} implementation that forwards telemetry to a {@link BasicLogger}.
|
|
9
|
+
*
|
|
10
|
+
* This is intentionally named *Sink* (not "adapter") to distinguish it from host logging bridges
|
|
11
|
+
* such as the CLI Pino bundle: it consumes telemetry events and writes them to the injected logger.
|
|
12
|
+
*/
|
|
13
|
+
export interface TelemetryLoggerSinkOptions {
|
|
8
14
|
logger?: BasicLogger;
|
|
9
15
|
name?: string;
|
|
10
16
|
enabled?: boolean | (() => boolean);
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
export class
|
|
19
|
+
export class TelemetryLoggerSink implements ITelemetryAdapter {
|
|
14
20
|
readonly name: string;
|
|
15
21
|
|
|
16
22
|
private readonly logger?: BasicLogger;
|
|
17
23
|
private readonly enabled: boolean | (() => boolean);
|
|
18
24
|
|
|
19
|
-
constructor(options:
|
|
20
|
-
this.name = options.name ?? "
|
|
25
|
+
constructor(options: TelemetryLoggerSinkOptions = {}) {
|
|
26
|
+
this.name = options.name ?? "TelemetryLoggerSink";
|
|
21
27
|
this.logger = options.logger;
|
|
22
28
|
this.enabled = options.enabled ?? true;
|
|
23
29
|
}
|
|
@@ -26,16 +32,17 @@ export class LoggerTelemetryAdapter implements ITelemetryAdapter {
|
|
|
26
32
|
if (!this.isEnabled()) {
|
|
27
33
|
return;
|
|
28
34
|
}
|
|
29
|
-
this.logger?.
|
|
30
|
-
|
|
35
|
+
this.logger?.log("telemetry.event", {
|
|
36
|
+
telemetrySink: this.name,
|
|
31
37
|
event,
|
|
32
38
|
properties,
|
|
33
39
|
});
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
emitRequired(event: string, properties?: TelemetryProperties): void {
|
|
37
|
-
this.logger?.
|
|
38
|
-
|
|
43
|
+
this.logger?.log("telemetry.required_event", {
|
|
44
|
+
telemetrySink: this.name,
|
|
45
|
+
severity: "warn",
|
|
39
46
|
event,
|
|
40
47
|
properties,
|
|
41
48
|
});
|
|
@@ -51,8 +58,8 @@ export class LoggerTelemetryAdapter implements ITelemetryAdapter {
|
|
|
51
58
|
if (!required && !this.isEnabled()) {
|
|
52
59
|
return;
|
|
53
60
|
}
|
|
54
|
-
this.logger?.debug
|
|
55
|
-
|
|
61
|
+
this.logger?.debug("telemetry.metric", {
|
|
62
|
+
telemetrySink: this.name,
|
|
56
63
|
instrument: "counter",
|
|
57
64
|
name,
|
|
58
65
|
value,
|
|
@@ -72,8 +79,8 @@ export class LoggerTelemetryAdapter implements ITelemetryAdapter {
|
|
|
72
79
|
if (!required && !this.isEnabled()) {
|
|
73
80
|
return;
|
|
74
81
|
}
|
|
75
|
-
this.logger?.debug
|
|
76
|
-
|
|
82
|
+
this.logger?.debug("telemetry.metric", {
|
|
83
|
+
telemetrySink: this.name,
|
|
77
84
|
instrument: "histogram",
|
|
78
85
|
name,
|
|
79
86
|
value,
|
|
@@ -93,8 +100,8 @@ export class LoggerTelemetryAdapter implements ITelemetryAdapter {
|
|
|
93
100
|
if (!required && !this.isEnabled()) {
|
|
94
101
|
return;
|
|
95
102
|
}
|
|
96
|
-
this.logger?.debug
|
|
97
|
-
|
|
103
|
+
this.logger?.debug("telemetry.metric", {
|
|
104
|
+
telemetrySink: this.name,
|
|
98
105
|
instrument: "gauge",
|
|
99
106
|
name,
|
|
100
107
|
value,
|
|
@@ -53,8 +53,7 @@ describe("TelemetryService", () => {
|
|
|
53
53
|
it("mirrors telemetry events into the logger when provided", () => {
|
|
54
54
|
const logger: BasicLogger = {
|
|
55
55
|
debug: vi.fn(),
|
|
56
|
-
|
|
57
|
-
warn: vi.fn(),
|
|
56
|
+
log: vi.fn(),
|
|
58
57
|
error: vi.fn(),
|
|
59
58
|
};
|
|
60
59
|
const service = new TelemetryService({
|
|
@@ -75,10 +74,10 @@ describe("TelemetryService", () => {
|
|
|
75
74
|
sessionId: "session-1",
|
|
76
75
|
});
|
|
77
76
|
|
|
78
|
-
expect(logger.
|
|
77
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
79
78
|
"telemetry.event",
|
|
80
79
|
expect.objectContaining({
|
|
81
|
-
|
|
80
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
82
81
|
event: "session.started",
|
|
83
82
|
properties: expect.objectContaining({
|
|
84
83
|
sessionId: "session-1",
|
|
@@ -87,10 +86,11 @@ describe("TelemetryService", () => {
|
|
|
87
86
|
}),
|
|
88
87
|
}),
|
|
89
88
|
);
|
|
90
|
-
expect(logger.
|
|
89
|
+
expect(logger.log).toHaveBeenCalledWith(
|
|
91
90
|
"telemetry.required_event",
|
|
92
91
|
expect.objectContaining({
|
|
93
|
-
|
|
92
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
93
|
+
severity: "warn",
|
|
94
94
|
event: "user.opt_out",
|
|
95
95
|
properties: expect.objectContaining({
|
|
96
96
|
reason: "manual",
|
|
@@ -101,7 +101,7 @@ describe("TelemetryService", () => {
|
|
|
101
101
|
expect(logger.debug).toHaveBeenCalledWith(
|
|
102
102
|
"telemetry.metric",
|
|
103
103
|
expect.objectContaining({
|
|
104
|
-
|
|
104
|
+
telemetrySink: "TelemetryLoggerSink",
|
|
105
105
|
instrument: "counter",
|
|
106
106
|
name: "cline.session.starts.total",
|
|
107
107
|
}),
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
TelemetryProperties,
|
|
6
6
|
} from "@clinebot/shared";
|
|
7
7
|
import type { ITelemetryAdapter } from "./ITelemetryAdapter";
|
|
8
|
-
import {
|
|
8
|
+
import { TelemetryLoggerSink } from "./TelemetryLoggerSink";
|
|
9
9
|
|
|
10
10
|
export interface TelemetryServiceOptions {
|
|
11
11
|
adapters?: ITelemetryAdapter[];
|
|
@@ -24,9 +24,7 @@ export class TelemetryService implements ITelemetryService {
|
|
|
24
24
|
constructor(options: TelemetryServiceOptions = {}) {
|
|
25
25
|
this.adapters = [...(options.adapters ?? [])];
|
|
26
26
|
if (options.logger) {
|
|
27
|
-
this.adapters.push(
|
|
28
|
-
new LoggerTelemetryAdapter({ logger: options.logger }),
|
|
29
|
-
);
|
|
27
|
+
this.adapters.push(new TelemetryLoggerSink({ logger: options.logger }));
|
|
30
28
|
}
|
|
31
29
|
this.metadata = { ...(options.metadata ?? {}) };
|
|
32
30
|
this.distinctId = options.distinctId;
|