@axonflow/openclaw 1.3.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +56 -6
- package/dist/audit.d.ts +2 -2
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +2 -2
- package/dist/audit.js.map +1 -1
- package/dist/axonflow-client.d.ts +22 -0
- package/dist/axonflow-client.d.ts.map +1 -1
- package/dist/axonflow-client.js +56 -0
- package/dist/axonflow-client.js.map +1 -1
- package/dist/cache-dir.d.ts +34 -0
- package/dist/cache-dir.d.ts.map +1 -0
- package/dist/cache-dir.js +106 -0
- package/dist/cache-dir.js.map +1 -0
- package/dist/client-ref.d.ts +19 -0
- package/dist/client-ref.d.ts.map +1 -0
- package/dist/client-ref.js +16 -0
- package/dist/client-ref.js.map +1 -0
- package/dist/community-saas-bootstrap.d.ts +58 -0
- package/dist/community-saas-bootstrap.d.ts.map +1 -0
- package/dist/community-saas-bootstrap.js +281 -0
- package/dist/community-saas-bootstrap.js.map +1 -0
- package/dist/config.d.ts +26 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +60 -30
- package/dist/config.js.map +1 -1
- package/dist/governance.d.ts +3 -2
- package/dist/governance.d.ts.map +1 -1
- package/dist/governance.js +2 -2
- package/dist/governance.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +123 -12
- package/dist/index.js.map +1 -1
- package/dist/llm-audit.d.ts +3 -3
- package/dist/llm-audit.d.ts.map +1 -1
- package/dist/llm-audit.js +3 -3
- package/dist/llm-audit.js.map +1 -1
- package/dist/message-guard.d.ts +2 -2
- package/dist/message-guard.d.ts.map +1 -1
- package/dist/message-guard.js +2 -2
- package/dist/message-guard.js.map +1 -1
- package/dist/plugin-version-check.d.ts +50 -0
- package/dist/plugin-version-check.d.ts.map +1 -0
- package/dist/plugin-version-check.js +89 -0
- package/dist/plugin-version-check.js.map +1 -0
- package/dist/telemetry-config.d.ts +5 -3
- package/dist/telemetry-config.d.ts.map +1 -1
- package/dist/telemetry-config.js +1 -15
- package/dist/telemetry-config.js.map +1 -1
- package/dist/telemetry.d.ts +37 -17
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +146 -49
- package/dist/telemetry.js.map +1 -1
- package/openclaw.plugin.json +3 -6
- package/package.json +9 -5
package/dist/index.js
CHANGED
|
@@ -30,16 +30,21 @@
|
|
|
30
30
|
* Outbound messages ARE scanned via message_sending. See upstream issue
|
|
31
31
|
* for async hook support.
|
|
32
32
|
*/
|
|
33
|
+
import * as fs from "fs";
|
|
34
|
+
import * as path from "path";
|
|
33
35
|
import { AxonFlowClient } from "./axonflow-client.js";
|
|
36
|
+
import { axonflowCacheDir } from "./cache-dir.js";
|
|
34
37
|
import { resolveConfig } from "./config.js";
|
|
35
38
|
import { createBeforeToolCallHandler } from "./governance.js";
|
|
36
39
|
import { createAfterToolCallHandler } from "./audit.js";
|
|
37
40
|
import { createMessageSendingHandler } from "./message-guard.js";
|
|
38
41
|
import { createLlmInputHandler, createLlmOutputHandler } from "./llm-audit.js";
|
|
39
42
|
import { sendTelemetryPing } from "./telemetry.js";
|
|
43
|
+
import { bootstrapCommunitySaas } from "./community-saas-bootstrap.js";
|
|
40
44
|
import { resetMetrics } from "./metrics.js";
|
|
45
|
+
import { runPluginVersionCheck } from "./plugin-version-check.js";
|
|
41
46
|
/** Plugin version — update before each release. */
|
|
42
|
-
export const VERSION = "
|
|
47
|
+
export const VERSION = "2.0.0";
|
|
43
48
|
// Re-export for external consumers
|
|
44
49
|
export { AxonFlowClient } from "./axonflow-client.js";
|
|
45
50
|
export { resolveConfig, shouldGovernTool } from "./config.js";
|
|
@@ -54,13 +59,65 @@ export { getMetrics } from "./metrics.js";
|
|
|
54
59
|
*/
|
|
55
60
|
export function registerAxonFlowGovernance(api) {
|
|
56
61
|
const config = resolveConfig(api.pluginConfig);
|
|
57
|
-
const client = new AxonFlowClient(config);
|
|
58
62
|
// Reset metrics on each registration (handles hot-reload)
|
|
59
63
|
resetMetrics();
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
// Mode-clarity canary — emitted on every plugin init so users always know
|
|
65
|
+
// which AxonFlow they're connected to. The Gate 4 mode-clarity test
|
|
66
|
+
// (tests/mode-clarity.test.ts) parses this exact line and asserts
|
|
67
|
+
// URL + mode match the resolved config.
|
|
68
|
+
api.logger.info(`[AxonFlow] Connected to AxonFlow at ${config.endpoint} (mode=${config.mode})`);
|
|
69
|
+
// In community-saas mode, register asynchronously against try.getaxonflow.com
|
|
70
|
+
// and override the client credentials with the bootstrapped values once
|
|
71
|
+
// they arrive. The startup health check + the first hook fire happen
|
|
72
|
+
// immediately; if the bootstrap is still pending when a hook fires, the
|
|
73
|
+
// existing AxonFlowClient handles the (transient) 401 and retries with
|
|
74
|
+
// the new credentials on the next call once they're loaded.
|
|
75
|
+
// Mutable holder so the bootstrap reassignment below is visible to every
|
|
76
|
+
// hook factory we register. Hook factories close over `clientRef` and read
|
|
77
|
+
// `clientRef.current.<method>(...)` so swapping in a freshly-credentialled
|
|
78
|
+
// client after the async bootstrap completes propagates immediately.
|
|
79
|
+
// Without this indirection the bootstrap would silently no-op for hooks
|
|
80
|
+
// (they'd keep using the empty-credential client they were registered with).
|
|
81
|
+
const clientRef = { current: new AxonFlowClient(config) };
|
|
82
|
+
if (config.mode === "community-saas") {
|
|
83
|
+
void bootstrapCommunitySaas({
|
|
84
|
+
endpoint: config.endpoint,
|
|
85
|
+
pluginVersion: VERSION,
|
|
86
|
+
}).then((result) => {
|
|
87
|
+
if (!result || result.source === "failed" || result.source === "rate-limited") {
|
|
88
|
+
const detail = result?.source === "rate-limited"
|
|
89
|
+
? "rate-limited (will retry)"
|
|
90
|
+
: "failed (network error or non-2xx response)";
|
|
91
|
+
const msg = `AxonFlow Community SaaS registration ${detail}. Tool calls will fail-${config.onError === "allow" ? "open (allow through)" : "closed (block)"} until registration succeeds.`;
|
|
92
|
+
if (api.logger.warn) {
|
|
93
|
+
api.logger.warn(msg);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
api.logger.error(msg);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const enriched = {
|
|
101
|
+
...config,
|
|
102
|
+
endpoint: result.endpoint,
|
|
103
|
+
clientId: result.clientId,
|
|
104
|
+
clientSecret: result.clientSecret,
|
|
105
|
+
};
|
|
106
|
+
clientRef.current = new AxonFlowClient(enriched);
|
|
107
|
+
api.logger.info(`[AxonFlow] Community SaaS registration ${result.source === "fresh-registration" ? "complete" : "loaded from cache"} (tenant=${result.clientId.slice(0, 16)}...)`);
|
|
108
|
+
}).catch(() => {
|
|
109
|
+
// Silent — bootstrap should never block plugin registration. The
|
|
110
|
+
// governance handlers will fail-open or fail-closed per onError config.
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// One-time positive disclosure on first Community-SaaS connection.
|
|
114
|
+
// Stamped at axonflowCacheDir()/openclaw-plugin-disclosure-shown so it
|
|
115
|
+
// fires exactly once per install (separate stamp from the heartbeat).
|
|
116
|
+
if (config.mode === "community-saas") {
|
|
117
|
+
showCommunitySaasDisclosureOnce(api);
|
|
118
|
+
}
|
|
62
119
|
// Startup health check (fire-and-forget, non-blocking)
|
|
63
|
-
void
|
|
120
|
+
void clientRef.current.healthCheck().then((healthy) => {
|
|
64
121
|
if (healthy) {
|
|
65
122
|
api.logger.info(`AxonFlow connected: ${config.endpoint}`);
|
|
66
123
|
}
|
|
@@ -76,30 +133,83 @@ export function registerAxonFlowGovernance(api) {
|
|
|
76
133
|
}).catch(() => {
|
|
77
134
|
// Silent — health check should never prevent plugin registration
|
|
78
135
|
});
|
|
136
|
+
// Plugin/platform version compatibility check (fire-and-forget,
|
|
137
|
+
// platform 7.5.0+). Mirrors what the SDKs already do at startup —
|
|
138
|
+
// emits a one-time upgrade warning when the plugin's own version is
|
|
139
|
+
// below the floor the platform expects, stays silent otherwise.
|
|
140
|
+
// Failure modes (network error, older platform, malformed response)
|
|
141
|
+
// are swallowed by runPluginVersionCheck — never blocks startup.
|
|
142
|
+
void runPluginVersionCheck(clientRef.current, VERSION, api.logger);
|
|
79
143
|
// Hook 1: Input governance (before tool execution)
|
|
80
|
-
const beforeToolCall = createBeforeToolCallHandler(
|
|
144
|
+
const beforeToolCall = createBeforeToolCallHandler(clientRef, config);
|
|
81
145
|
api.on("before_tool_call", beforeToolCall, { priority: 10 });
|
|
82
146
|
// Hook 2: Audit logging (after tool execution)
|
|
83
|
-
const afterToolCall = createAfterToolCallHandler(
|
|
147
|
+
const afterToolCall = createAfterToolCallHandler(clientRef, config);
|
|
84
148
|
api.on("after_tool_call", afterToolCall, { priority: 90 });
|
|
85
149
|
// Hook 3: Outbound message governance (before message reaches user)
|
|
86
|
-
const messageSending = createMessageSendingHandler(
|
|
150
|
+
const messageSending = createMessageSendingHandler(clientRef, config);
|
|
87
151
|
api.on("message_sending", messageSending, { priority: 10 });
|
|
88
152
|
// Hook 4-5: LLM call audit (observe-only, cannot block/modify)
|
|
89
153
|
const llmCallState = new Map();
|
|
90
|
-
const llmInput = createLlmInputHandler(
|
|
154
|
+
const llmInput = createLlmInputHandler(clientRef, config, llmCallState);
|
|
91
155
|
api.on("llm_input", llmInput, { priority: 90 });
|
|
92
|
-
const llmOutput = createLlmOutputHandler(
|
|
156
|
+
const llmOutput = createLlmOutputHandler(clientRef, config, llmCallState);
|
|
93
157
|
api.on("llm_output", llmOutput, { priority: 90 });
|
|
94
|
-
// Telemetry (fire-and-forget
|
|
95
|
-
|
|
158
|
+
// Telemetry — 7-day heartbeat (fire-and-forget; opt out with
|
|
159
|
+
// AXONFLOW_TELEMETRY=off). The promise is intentionally not awaited.
|
|
160
|
+
void sendTelemetryPing({
|
|
96
161
|
endpoint: config.endpoint,
|
|
97
162
|
pluginVersion: VERSION,
|
|
98
163
|
hookCount: 5,
|
|
99
164
|
highRiskToolCount: (config.highRiskTools ?? []).length,
|
|
100
165
|
onError: config.onError ?? "block",
|
|
166
|
+
mode: config.mode,
|
|
101
167
|
});
|
|
102
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* One-time positive disclosure when first connecting to Community SaaS.
|
|
171
|
+
* Stamped at axonflowCacheDir()/openclaw-plugin-disclosure-shown so it
|
|
172
|
+
* fires exactly once per install. Failures (no writable cache dir, etc.)
|
|
173
|
+
* fall through silently — the disclosure is best-effort and never blocks
|
|
174
|
+
* plugin registration.
|
|
175
|
+
*/
|
|
176
|
+
function showCommunitySaasDisclosureOnce(api) {
|
|
177
|
+
const cacheDir = axonflowCacheDir();
|
|
178
|
+
if (!cacheDir)
|
|
179
|
+
return;
|
|
180
|
+
const stamp = path.join(cacheDir, "openclaw-plugin-disclosure-shown");
|
|
181
|
+
try {
|
|
182
|
+
fs.statSync(stamp);
|
|
183
|
+
return; // already shown
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// not stamped yet — proceed
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 });
|
|
190
|
+
if (process.platform !== "win32") {
|
|
191
|
+
try {
|
|
192
|
+
fs.chmodSync(cacheDir, 0o700);
|
|
193
|
+
}
|
|
194
|
+
catch { /* best effort */ }
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
api.logger.info("[AxonFlow] Connected to AxonFlow Community SaaS at https://try.getaxonflow.com.\n" +
|
|
201
|
+
"Intended for basic testing and evaluation. For real workflows, real systems,\n" +
|
|
202
|
+
"or sensitive data, we recommend self-hosting AxonFlow from day one:\n" +
|
|
203
|
+
" https://docs.getaxonflow.com/quickstart\n" +
|
|
204
|
+
"Anonymous telemetry: weekly heartbeat. Opt out: AXONFLOW_TELEMETRY=off");
|
|
205
|
+
try {
|
|
206
|
+
fs.writeFileSync(stamp, "", { mode: 0o600 });
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// best effort; if we can't stamp, the message will fire again
|
|
210
|
+
// next time. Acceptable trade-off vs not surfacing it.
|
|
211
|
+
}
|
|
212
|
+
}
|
|
103
213
|
/**
|
|
104
214
|
* Default export for OpenClaw plugin loader.
|
|
105
215
|
*
|
|
@@ -113,4 +223,5 @@ export default {
|
|
|
113
223
|
description: "Policy enforcement for tool inputs, PII scanning on outbound messages, and audit trails for OpenClaw",
|
|
114
224
|
register: registerAxonFlowGovernance,
|
|
115
225
|
};
|
|
226
|
+
// CI re-trigger: 1777491400
|
|
116
227
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/llm-audit.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* observe-only (cannot block or modify), so they provide audit
|
|
7
7
|
* evidence, not governance.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { ClientRef } from "./client-ref.js";
|
|
10
10
|
import type { AxonFlowPluginConfig } from "./config.js";
|
|
11
11
|
/** Shared state for correlating llm_input with llm_output. */
|
|
12
12
|
interface LLMCallState {
|
|
@@ -21,7 +21,7 @@ interface LLMCallState {
|
|
|
21
21
|
* Records the prompt, model, and provider at the start of each LLM call.
|
|
22
22
|
* Stores state by runId for correlation with the llm_output handler.
|
|
23
23
|
*/
|
|
24
|
-
export declare function createLlmInputHandler(
|
|
24
|
+
export declare function createLlmInputHandler(_clientRef: ClientRef, _config: AxonFlowPluginConfig, callState: Map<string, LLMCallState>): (event: {
|
|
25
25
|
runId: string;
|
|
26
26
|
sessionId: string;
|
|
27
27
|
provider: string;
|
|
@@ -38,7 +38,7 @@ export declare function createLlmInputHandler(_client: AxonFlowClient, _config:
|
|
|
38
38
|
* audit entry to AxonFlow (provider, model, prompt summary, response
|
|
39
39
|
* summary, token usage, latency).
|
|
40
40
|
*/
|
|
41
|
-
export declare function createLlmOutputHandler(
|
|
41
|
+
export declare function createLlmOutputHandler(clientRef: ClientRef, _config: AxonFlowPluginConfig, callState: Map<string, LLMCallState>): (event: {
|
|
42
42
|
runId: string;
|
|
43
43
|
sessionId: string;
|
|
44
44
|
provider: string;
|
package/dist/llm-audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-audit.d.ts","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"llm-audit.d.ts","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxD,8DAA8D;AAC9D,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,EACrB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,IAE5B,OAAO;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB,KAAG,IAAI,CAmBT;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,IAEtB,OAAO;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,KAAG,OAAO,CAAC,IAAI,CAAC,CAyBlB"}
|
package/dist/llm-audit.js
CHANGED
|
@@ -13,7 +13,7 @@ import { recordAuditEventSent } from "./metrics.js";
|
|
|
13
13
|
* Records the prompt, model, and provider at the start of each LLM call.
|
|
14
14
|
* Stores state by runId for correlation with the llm_output handler.
|
|
15
15
|
*/
|
|
16
|
-
export function createLlmInputHandler(
|
|
16
|
+
export function createLlmInputHandler(_clientRef, _config, callState) {
|
|
17
17
|
return (event) => {
|
|
18
18
|
callState.set(event.runId, {
|
|
19
19
|
provider: event.provider,
|
|
@@ -40,14 +40,14 @@ export function createLlmInputHandler(_client, _config, callState) {
|
|
|
40
40
|
* audit entry to AxonFlow (provider, model, prompt summary, response
|
|
41
41
|
* summary, token usage, latency).
|
|
42
42
|
*/
|
|
43
|
-
export function createLlmOutputHandler(
|
|
43
|
+
export function createLlmOutputHandler(clientRef, _config, callState) {
|
|
44
44
|
return async (event) => {
|
|
45
45
|
const inputState = callState.get(event.runId);
|
|
46
46
|
callState.delete(event.runId);
|
|
47
47
|
const responseSummary = event.assistantTexts.join(" ").slice(0, 200);
|
|
48
48
|
const latencyMs = inputState ? Date.now() - inputState.startMs : 0;
|
|
49
49
|
try {
|
|
50
|
-
await
|
|
50
|
+
await clientRef.current.auditLLMCall(inputState?.provider ?? event.provider, inputState?.model ?? event.model, inputState?.prompt ?? "", responseSummary, {
|
|
51
51
|
prompt_tokens: event.usage?.input ?? 0,
|
|
52
52
|
completion_tokens: event.usage?.output ?? 0,
|
|
53
53
|
total_tokens: event.usage?.total ?? 0,
|
package/dist/llm-audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-audit.js","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUpD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,
|
|
1
|
+
{"version":3,"file":"llm-audit.js","sourceRoot":"","sources":["../src/llm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUpD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAqB,EACrB,OAA6B,EAC7B,SAAoC;IAEpC,OAAO,CAAC,KASP,EAAQ,EAAE;QACT,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE;YACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;YAClC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;SACpB,CAAC,CAAC;QAEH,gEAAgE;QAChE,oEAAoE;QACpE,4CAA4C;QAC5C,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;YACnC,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,UAAU,EAAE,CAAC;gBACnC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAoB,EACpB,OAA6B,EAC7B,SAAoC;IAEpC,OAAO,KAAK,EAAE,KAYb,EAAiB,EAAE;QAClB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAEnE,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,OAAO,CAAC,YAAY,CAClC,UAAU,EAAE,QAAQ,IAAI,KAAK,CAAC,QAAQ,EACtC,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC,KAAK,EAChC,UAAU,EAAE,MAAM,IAAI,EAAE,EACxB,eAAe,EACf;gBACE,aAAa,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC;gBACtC,iBAAiB,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;gBAC3C,YAAY,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC;aACtC,EACD,SAAS,CACV,CAAC;YACF,oBAAoB,EAAE,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,+BAA+B;QACjC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/message-guard.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Discord, Slack, WhatsApp). Can cancel messages containing PII/secrets
|
|
6
6
|
* or redact sensitive content.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { ClientRef } from "./client-ref.js";
|
|
9
9
|
import type { AxonFlowPluginConfig } from "./config.js";
|
|
10
10
|
/**
|
|
11
11
|
* Create the message_sending hook handler.
|
|
@@ -14,7 +14,7 @@ import type { AxonFlowPluginConfig } from "./config.js";
|
|
|
14
14
|
* Can cancel (prevent sending) or redact (modify content) before delivery.
|
|
15
15
|
* Respects config.onError for fail-open/fail-closed behavior.
|
|
16
16
|
*/
|
|
17
|
-
export declare function createMessageSendingHandler(
|
|
17
|
+
export declare function createMessageSendingHandler(clientRef: ClientRef, config: AxonFlowPluginConfig): (event: {
|
|
18
18
|
to: string;
|
|
19
19
|
content: string;
|
|
20
20
|
metadata?: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-guard.d.ts","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"message-guard.d.ts","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAQxD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,oBAAoB,IAEd,OAAO;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAG,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAAC,CAyChE"}
|
package/dist/message-guard.js
CHANGED
|
@@ -13,7 +13,7 @@ import { recordMessageScanned, recordMessageCancelled, recordMessageRedacted, re
|
|
|
13
13
|
* Can cancel (prevent sending) or redact (modify content) before delivery.
|
|
14
14
|
* Respects config.onError for fail-open/fail-closed behavior.
|
|
15
15
|
*/
|
|
16
|
-
export function createMessageSendingHandler(
|
|
16
|
+
export function createMessageSendingHandler(clientRef, config) {
|
|
17
17
|
return async (event) => {
|
|
18
18
|
if (!event.content) {
|
|
19
19
|
return undefined;
|
|
@@ -21,7 +21,7 @@ export function createMessageSendingHandler(client, config) {
|
|
|
21
21
|
recordMessageScanned();
|
|
22
22
|
let check;
|
|
23
23
|
try {
|
|
24
|
-
check = await
|
|
24
|
+
check = await clientRef.current.mcpCheckOutput("openclaw.message_sending", event.content);
|
|
25
25
|
}
|
|
26
26
|
catch {
|
|
27
27
|
recordGovernanceError();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-guard.js","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,
|
|
1
|
+
{"version":3,"file":"message-guard.js","sourceRoot":"","sources":["../src/message-guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAoB,EACpB,MAA4B;IAE5B,OAAO,KAAK,EAAE,KAIb,EAA+D,EAAE;QAChE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,oBAAoB,EAAE,CAAC;QAEvB,IAAI,KAAK,CAAC;QACV,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,cAAc,CAC5C,0BAA0B,EAC1B,KAAK,CAAC,OAAO,CACd,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB,EAAE,CAAC;YACxB,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;gBAC/B,OAAO,SAAS,CAAC,CAAC,8CAA8C;YAClE,CAAC;YACD,sBAAsB,EAAE,CAAC;YACzB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,sBAAsB,EAAE,CAAC;YACzB,OAAO;gBACL,MAAM,EAAE,IAAI;aACb,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAChC,qBAAqB,EAAE,CAAC;YACxB,OAAO;gBACL,OAAO,EACL,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ;oBACrC,CAAC,CAAC,KAAK,CAAC,aAAa;oBACrB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC;aAC1C,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin/platform version compatibility check.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the SDK pattern: the plugin queries the platform's /health,
|
|
5
|
+
* reads `plugin_compatibility.min_plugin_version["openclaw"]`, and
|
|
6
|
+
* logs a one-time upgrade warning when its own runtime version is
|
|
7
|
+
* below the floor the platform expects.
|
|
8
|
+
*
|
|
9
|
+
* The check is fire-and-forget — if the platform doesn't advertise
|
|
10
|
+
* the field (older platforms) or the request fails, the plugin keeps
|
|
11
|
+
* working silently. The warning is informational; nothing in the
|
|
12
|
+
* plugin's hot path depends on the result.
|
|
13
|
+
*
|
|
14
|
+
* Plugin id `"openclaw"` matches the canonical id in
|
|
15
|
+
* `axonflow-enterprise/platform/agent/integration_activation.go` and
|
|
16
|
+
* `getPluginCompatibility()` in capabilities.go.
|
|
17
|
+
*/
|
|
18
|
+
import type { AxonFlowClient } from "./axonflow-client.js";
|
|
19
|
+
export declare const PLUGIN_ID = "openclaw";
|
|
20
|
+
export interface VersionCheckLogger {
|
|
21
|
+
warn?: (msg: string) => void;
|
|
22
|
+
info?: (msg: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Compare two semver-ish version strings.
|
|
26
|
+
*
|
|
27
|
+
* Returns -1 if `a < b`, 0 if equal, 1 if `a > b`. Treats invalid /
|
|
28
|
+
* pre-release / build-metadata segments by stripping them and comparing
|
|
29
|
+
* the major.minor.patch numeric prefix only — adequate for the
|
|
30
|
+
* downgrade-warning gate, which only cares about ordering of the
|
|
31
|
+
* canonical numeric tuple.
|
|
32
|
+
*/
|
|
33
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
34
|
+
/**
|
|
35
|
+
* Run the plugin/platform version compatibility check and emit a
|
|
36
|
+
* single log line on the result.
|
|
37
|
+
*
|
|
38
|
+
* - If the platform doesn't advertise plugin_compatibility (older
|
|
39
|
+
* platform or unparseable response), no log line is emitted —
|
|
40
|
+
* absence of signal is not a regression.
|
|
41
|
+
* - If the plugin runtime version is below `min_plugin_version`,
|
|
42
|
+
* logger.warn is called with an upgrade hint.
|
|
43
|
+
* - If the plugin is at or above the floor but below the recommended
|
|
44
|
+
* version, logger.info is called (informational).
|
|
45
|
+
*
|
|
46
|
+
* Exceptions are swallowed — the check must not prevent plugin
|
|
47
|
+
* startup or affect the hook hot path.
|
|
48
|
+
*/
|
|
49
|
+
export declare function runPluginVersionCheck(client: Pick<AxonFlowClient, "getPluginCompatibility">, pluginVersion: string, logger: VersionCheckLogger): Promise<void>;
|
|
50
|
+
//# sourceMappingURL=plugin-version-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-version-check.d.ts","sourceRoot":"","sources":["../src/plugin-version-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,eAAO,MAAM,SAAS,aAAa,CAAC;AAEpC,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAa5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,wBAAwB,CAAC,EACtD,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin/platform version compatibility check.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the SDK pattern: the plugin queries the platform's /health,
|
|
5
|
+
* reads `plugin_compatibility.min_plugin_version["openclaw"]`, and
|
|
6
|
+
* logs a one-time upgrade warning when its own runtime version is
|
|
7
|
+
* below the floor the platform expects.
|
|
8
|
+
*
|
|
9
|
+
* The check is fire-and-forget — if the platform doesn't advertise
|
|
10
|
+
* the field (older platforms) or the request fails, the plugin keeps
|
|
11
|
+
* working silently. The warning is informational; nothing in the
|
|
12
|
+
* plugin's hot path depends on the result.
|
|
13
|
+
*
|
|
14
|
+
* Plugin id `"openclaw"` matches the canonical id in
|
|
15
|
+
* `axonflow-enterprise/platform/agent/integration_activation.go` and
|
|
16
|
+
* `getPluginCompatibility()` in capabilities.go.
|
|
17
|
+
*/
|
|
18
|
+
export const PLUGIN_ID = "openclaw";
|
|
19
|
+
/**
|
|
20
|
+
* Compare two semver-ish version strings.
|
|
21
|
+
*
|
|
22
|
+
* Returns -1 if `a < b`, 0 if equal, 1 if `a > b`. Treats invalid /
|
|
23
|
+
* pre-release / build-metadata segments by stripping them and comparing
|
|
24
|
+
* the major.minor.patch numeric prefix only — adequate for the
|
|
25
|
+
* downgrade-warning gate, which only cares about ordering of the
|
|
26
|
+
* canonical numeric tuple.
|
|
27
|
+
*/
|
|
28
|
+
export function compareVersions(a, b) {
|
|
29
|
+
const parse = (v) => v.replace(/^v/, "").split(/[-+]/, 1)[0].split(".").map((p) => Number(p) | 0);
|
|
30
|
+
const pa = parse(a);
|
|
31
|
+
const pb = parse(b);
|
|
32
|
+
const len = Math.max(pa.length, pb.length);
|
|
33
|
+
for (let i = 0; i < len; i++) {
|
|
34
|
+
const av = pa[i] ?? 0;
|
|
35
|
+
const bv = pb[i] ?? 0;
|
|
36
|
+
if (av < bv)
|
|
37
|
+
return -1;
|
|
38
|
+
if (av > bv)
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Run the plugin/platform version compatibility check and emit a
|
|
45
|
+
* single log line on the result.
|
|
46
|
+
*
|
|
47
|
+
* - If the platform doesn't advertise plugin_compatibility (older
|
|
48
|
+
* platform or unparseable response), no log line is emitted —
|
|
49
|
+
* absence of signal is not a regression.
|
|
50
|
+
* - If the plugin runtime version is below `min_plugin_version`,
|
|
51
|
+
* logger.warn is called with an upgrade hint.
|
|
52
|
+
* - If the plugin is at or above the floor but below the recommended
|
|
53
|
+
* version, logger.info is called (informational).
|
|
54
|
+
*
|
|
55
|
+
* Exceptions are swallowed — the check must not prevent plugin
|
|
56
|
+
* startup or affect the hook hot path.
|
|
57
|
+
*/
|
|
58
|
+
export async function runPluginVersionCheck(client, pluginVersion, logger) {
|
|
59
|
+
let compat;
|
|
60
|
+
try {
|
|
61
|
+
compat = await client.getPluginCompatibility();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!compat)
|
|
67
|
+
return;
|
|
68
|
+
const min = compat.minPluginVersion[PLUGIN_ID];
|
|
69
|
+
const recommended = compat.recommendedPluginVersion[PLUGIN_ID];
|
|
70
|
+
if (min && compareVersions(pluginVersion, min) < 0) {
|
|
71
|
+
const msg = `AxonFlow @axonflow/openclaw v${pluginVersion} is below the platform's ` +
|
|
72
|
+
`minimum supported version (v${min}). Upgrade with ` +
|
|
73
|
+
`\`npm install @axonflow/openclaw@latest\` — older releases may mis-handle ` +
|
|
74
|
+
`newer platform contract fields.`;
|
|
75
|
+
if (logger.warn) {
|
|
76
|
+
logger.warn(msg);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (recommended && compareVersions(pluginVersion, recommended) < 0) {
|
|
81
|
+
const msg = `AxonFlow @axonflow/openclaw v${pluginVersion} is below the recommended ` +
|
|
82
|
+
`version (v${recommended}). Plugin will keep working; upgrade for the ` +
|
|
83
|
+
`full feature set.`;
|
|
84
|
+
if (logger.info) {
|
|
85
|
+
logger.info(msg);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=plugin-version-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-version-check.js","sourceRoot":"","sources":["../src/plugin-version-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;AAOpC;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAC1B,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC;QACvB,IAAI,EAAE,GAAG,EAAE;YAAE,OAAO,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAsD,EACtD,aAAqB,EACrB,MAA0B;IAE1B,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,MAAM,CAAC,sBAAsB,EAAE,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,MAAM,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,MAAM,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAE/D,IAAI,GAAG,IAAI,eAAe,CAAC,aAAa,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,GAAG,GACP,gCAAgC,aAAa,2BAA2B;YACxE,+BAA+B,GAAG,kBAAkB;YACpD,4EAA4E;YAC5E,iCAAiC,CAAC;QACpC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,OAAO;IACT,CAAC;IACD,IAAI,WAAW,IAAI,eAAe,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,GAAG,GACP,gCAAgC,aAAa,4BAA4B;YACzE,aAAa,WAAW,+CAA+C;YACvE,mBAAmB,CAAC;QACtB,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export interface TelemetryConfig {
|
|
9
9
|
/**
|
|
10
|
-
* True if the user has opted out via AXONFLOW_TELEMETRY=off
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* True if the user has opted out via AXONFLOW_TELEMETRY=off.
|
|
11
|
+
*
|
|
12
|
+
* DO_NOT_TRACK is intentionally not honored: it is commonly inherited from
|
|
13
|
+
* host tools and developer environments, which makes it an unreliable
|
|
14
|
+
* expression of user intent for AxonFlow telemetry.
|
|
13
15
|
*/
|
|
14
16
|
optedOut: boolean;
|
|
15
17
|
/** Endpoint that receives the anonymous ping. Configurable for self-hosted checkpoint deployments. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry-config.d.ts","sourceRoot":"","sources":["../src/telemetry-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,eAAe;IAC9B
|
|
1
|
+
{"version":3,"file":"telemetry-config.d.ts","sourceRoot":"","sources":["../src/telemetry-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB,sGAAsG;IACtG,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,mBAAmB,IAAI,eAAe,CAYrD"}
|
package/dist/telemetry-config.js
CHANGED
|
@@ -11,22 +11,8 @@ export function loadTelemetryConfig() {
|
|
|
11
11
|
return { optedOut: false, checkpointUrl: DEFAULT_CHECKPOINT_URL };
|
|
12
12
|
}
|
|
13
13
|
const env = process.env;
|
|
14
|
-
const
|
|
15
|
-
const axonflowTelemetryOff = env.AXONFLOW_TELEMETRY?.trim().toLowerCase() === "off";
|
|
16
|
-
const optedOut = dntActive || axonflowTelemetryOff;
|
|
17
|
-
// Deprecation warning — fires only when DO_NOT_TRACK is the active control
|
|
18
|
-
// and AXONFLOW_TELEMETRY=off is NOT set. If both are set, the operator has
|
|
19
|
-
// already migrated to the canonical switch; no warning. Guarded to run at
|
|
20
|
-
// most once per plugin process via a module-level sentinel.
|
|
21
|
-
if (dntActive && !axonflowTelemetryOff && !doNotTrackDeprecationWarningShown) {
|
|
22
|
-
doNotTrackDeprecationWarningShown = true;
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.warn("[AxonFlow] DO_NOT_TRACK=1 is deprecated as an AxonFlow telemetry opt-out and will be removed after 2026-05-05 in the next major release. Set AXONFLOW_TELEMETRY=off to opt out going forward. See https://docs.getaxonflow.com/docs/telemetry for details.");
|
|
25
|
-
}
|
|
14
|
+
const optedOut = env.AXONFLOW_TELEMETRY?.trim().toLowerCase() === "off";
|
|
26
15
|
const checkpointUrl = env.AXONFLOW_CHECKPOINT_URL || DEFAULT_CHECKPOINT_URL;
|
|
27
16
|
return { optedOut, checkpointUrl };
|
|
28
17
|
}
|
|
29
|
-
// Module-level sentinel keeps the deprecation warning to one emission per
|
|
30
|
-
// process even if loadTelemetryConfig is called from multiple code paths.
|
|
31
|
-
let doNotTrackDeprecationWarningShown = false;
|
|
32
18
|
//# sourceMappingURL=telemetry-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry-config.js","sourceRoot":"","sources":["../src/telemetry-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,sBAAsB,GAAG,4CAA4C,CAAC;
|
|
1
|
+
{"version":3,"file":"telemetry-config.js","sourceRoot":"","sources":["../src/telemetry-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,sBAAsB,GAAG,4CAA4C,CAAC;AAe5E,MAAM,UAAU,mBAAmB;IACjC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAExB,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;IAExE,MAAM,aAAa,GAAG,GAAG,CAAC,uBAAuB,IAAI,sBAAsB,CAAC;IAE5E,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;AACrC,CAAC"}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Anonymous usage telemetry
|
|
2
|
+
* Anonymous usage telemetry — 7-day heartbeat (TypeScript).
|
|
3
3
|
*
|
|
4
|
-
* Sends
|
|
5
|
-
*
|
|
6
|
-
* and OpenClaw version. No PII, no tool arguments, no policy data.
|
|
4
|
+
* Sends an anonymous POST to checkpoint.getaxonflow.com on plugin
|
|
5
|
+
* initialization, at most once every 7 days per machine.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* after
|
|
11
|
-
*
|
|
7
|
+
* Design rules (per feedback_telemetry_heartbeat_design_rules.md):
|
|
8
|
+
* 1. Stamp-on-delivery, not stamp-on-attempt. Stamp file mtime
|
|
9
|
+
* advances ONLY after the HTTP POST returns 2xx. A transient
|
|
10
|
+
* network failure does not silence telemetry for 7 days.
|
|
11
|
+
* 2. In-flight gate via a per-process Promise. Concurrent plugin
|
|
12
|
+
* loads do not race to send duplicate pings.
|
|
13
|
+
* 3. Opt-out check FIRST, before any rate-limit or filesystem ops.
|
|
14
|
+
* AXONFLOW_TELEMETRY=off is re-evaluated every call.
|
|
15
|
+
* 4. mtime as the freshness source; stamp body holds instance_id.
|
|
16
|
+
* 5. Atomic stamp write: tmp + rename.
|
|
17
|
+
* 6. Persistent instance_id across heartbeats.
|
|
18
|
+
* 7. Defensive against future-dated stamps (clock skew → treat absent).
|
|
19
|
+
* 8. Cross-platform cache dir resolution (cache-dir.ts).
|
|
12
20
|
*
|
|
13
21
|
* Configuration resolution (opt-out flags and checkpoint URL) lives in
|
|
14
|
-
* telemetry-config.ts so this
|
|
22
|
+
* telemetry-config.ts so this module only handles the network-sending side.
|
|
15
23
|
*/
|
|
16
24
|
export interface TelemetryPayload {
|
|
17
25
|
sdk: string;
|
|
@@ -24,17 +32,29 @@ export interface TelemetryPayload {
|
|
|
24
32
|
features: string[];
|
|
25
33
|
instance_id: string;
|
|
26
34
|
}
|
|
27
|
-
|
|
28
|
-
* Send an anonymous telemetry ping on plugin initialization.
|
|
29
|
-
*
|
|
30
|
-
* Fire-and-forget: errors are silently swallowed, 3-second timeout
|
|
31
|
-
* prevents blocking. Never affects plugin behavior.
|
|
32
|
-
*/
|
|
33
|
-
export declare function sendTelemetryPing(options: {
|
|
35
|
+
interface SendOptions {
|
|
34
36
|
endpoint: string;
|
|
35
37
|
pluginVersion: string;
|
|
36
38
|
hookCount: number;
|
|
37
39
|
highRiskToolCount: number;
|
|
38
40
|
onError: string;
|
|
39
|
-
|
|
41
|
+
/** "community-saas" | "self-hosted" — set by the caller after resolveConfig. */
|
|
42
|
+
mode: string;
|
|
43
|
+
now?: () => Date;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Send an anonymous telemetry heartbeat. Concurrent calls are de-duplicated
|
|
47
|
+
* via a per-process in-flight gate; the second concurrent caller awaits the
|
|
48
|
+
* first's promise rather than firing a duplicate.
|
|
49
|
+
*
|
|
50
|
+
* Returns a promise that resolves when the heartbeat completes (success,
|
|
51
|
+
* skip, or failure). Production callers should treat as fire-and-forget;
|
|
52
|
+
* tests can await for assertions.
|
|
53
|
+
*/
|
|
54
|
+
export declare function sendTelemetryPing(options: SendOptions): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Test-only: clear the in-flight gate between tests.
|
|
57
|
+
*/
|
|
58
|
+
export declare function _resetTelemetryInFlightForTests(): void;
|
|
59
|
+
export {};
|
|
40
60
|
//# sourceMappingURL=telemetry.d.ts.map
|
package/dist/telemetry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAqCD,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrE;AAgID;;GAEG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
|