@axonflow/openclaw 1.3.2 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +111 -7
  3. package/dist/audit.d.ts +2 -2
  4. package/dist/audit.d.ts.map +1 -1
  5. package/dist/audit.js +2 -2
  6. package/dist/audit.js.map +1 -1
  7. package/dist/axonflow-client.d.ts +22 -0
  8. package/dist/axonflow-client.d.ts.map +1 -1
  9. package/dist/axonflow-client.js +56 -0
  10. package/dist/axonflow-client.js.map +1 -1
  11. package/dist/cache-dir.d.ts +34 -0
  12. package/dist/cache-dir.d.ts.map +1 -0
  13. package/dist/cache-dir.js +106 -0
  14. package/dist/cache-dir.js.map +1 -0
  15. package/dist/client-ref.d.ts +19 -0
  16. package/dist/client-ref.d.ts.map +1 -0
  17. package/dist/client-ref.js +16 -0
  18. package/dist/client-ref.js.map +1 -0
  19. package/dist/community-saas-bootstrap.d.ts +85 -0
  20. package/dist/community-saas-bootstrap.d.ts.map +1 -0
  21. package/dist/community-saas-bootstrap.js +256 -0
  22. package/dist/community-saas-bootstrap.js.map +1 -0
  23. package/dist/community-saas-context.d.ts +82 -0
  24. package/dist/community-saas-context.d.ts.map +1 -0
  25. package/dist/community-saas-context.js +196 -0
  26. package/dist/community-saas-context.js.map +1 -0
  27. package/dist/config.d.ts +26 -1
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js +60 -30
  30. package/dist/config.js.map +1 -1
  31. package/dist/governance.d.ts +3 -2
  32. package/dist/governance.d.ts.map +1 -1
  33. package/dist/governance.js +2 -2
  34. package/dist/governance.js.map +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +98 -12
  38. package/dist/index.js.map +1 -1
  39. package/dist/llm-audit.d.ts +3 -3
  40. package/dist/llm-audit.d.ts.map +1 -1
  41. package/dist/llm-audit.js +3 -3
  42. package/dist/llm-audit.js.map +1 -1
  43. package/dist/message-guard.d.ts +2 -2
  44. package/dist/message-guard.d.ts.map +1 -1
  45. package/dist/message-guard.js +2 -2
  46. package/dist/message-guard.js.map +1 -1
  47. package/dist/plugin-version-check.d.ts +50 -0
  48. package/dist/plugin-version-check.d.ts.map +1 -0
  49. package/dist/plugin-version-check.js +89 -0
  50. package/dist/plugin-version-check.js.map +1 -0
  51. package/dist/telemetry-config.d.ts +5 -3
  52. package/dist/telemetry-config.d.ts.map +1 -1
  53. package/dist/telemetry-config.js +1 -15
  54. package/dist/telemetry-config.js.map +1 -1
  55. package/dist/telemetry-context.d.ts +65 -0
  56. package/dist/telemetry-context.d.ts.map +1 -0
  57. package/dist/telemetry-context.js +116 -0
  58. package/dist/telemetry-context.js.map +1 -0
  59. package/dist/telemetry.d.ts +42 -18
  60. package/dist/telemetry.d.ts.map +1 -1
  61. package/dist/telemetry.js +113 -54
  62. package/dist/telemetry.js.map +1 -1
  63. package/openclaw.plugin.json +60 -6
  64. package/package.json +10 -5
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Mutable holder for the AxonFlowClient instance used by hook handlers.
3
+ *
4
+ * Hook factories close over the holder rather than the client value directly.
5
+ * That lets `registerAxonFlowGovernance` swap in a freshly-credentialled
6
+ * client after the asynchronous Community-SaaS bootstrap completes — every
7
+ * already-registered hook reads through `clientRef.current` and immediately
8
+ * sees the new credentials.
9
+ *
10
+ * Without this indirection the bootstrap reassignment is dead code: the
11
+ * handlers were called with the original empty-credential client when they
12
+ * were registered, and JavaScript captured that value by binding, not by
13
+ * reference to the outer `let client` slot.
14
+ */
15
+ import type { AxonFlowClient } from "./axonflow-client.js";
16
+ export interface ClientRef {
17
+ current: AxonFlowClient;
18
+ }
19
+ //# sourceMappingURL=client-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-ref.d.ts","sourceRoot":"","sources":["../src/client-ref.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;CACzB"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Mutable holder for the AxonFlowClient instance used by hook handlers.
3
+ *
4
+ * Hook factories close over the holder rather than the client value directly.
5
+ * That lets `registerAxonFlowGovernance` swap in a freshly-credentialled
6
+ * client after the asynchronous Community-SaaS bootstrap completes — every
7
+ * already-registered hook reads through `clientRef.current` and immediately
8
+ * sees the new credentials.
9
+ *
10
+ * Without this indirection the bootstrap reassignment is dead code: the
11
+ * handlers were called with the original empty-credential client when they
12
+ * were registered, and JavaScript captured that value by binding, not by
13
+ * reference to the outer `let client` slot.
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=client-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-ref.js","sourceRoot":"","sources":["../src/client-ref.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Community-SaaS first-run bootstrap (TypeScript).
3
+ *
4
+ * Mirrors the bash plugins' scripts/community-saas-bootstrap.sh contract:
5
+ * registers the plugin against try.getaxonflow.com on first run when the
6
+ * user has not provided explicit clientId/clientSecret, persists the
7
+ * resulting credential to a 0600 file under the user's config dir, and
8
+ * returns Basic-auth credentials the caller can hand to the AxonFlow client.
9
+ *
10
+ * Design rules:
11
+ * - Stamp-on-delivery: registration file is written ONLY after the
12
+ * POST returns 201 with a valid response body. A network failure
13
+ * leaves the previous (or absent) state untouched.
14
+ * - Atomic writes: temp + rename so a crash mid-write never produces
15
+ * a half-readable file.
16
+ * - In-flight gate: a per-process Promise-based lock so concurrent
17
+ * plugin loads don't both race to register.
18
+ * - File permissions: 0700 directory, 0600 file. The file holds the
19
+ * plain-text credential; world-readable would be a security bug.
20
+ * - 429 (registration rate-limit) → write a short backoff stamp and
21
+ * return null. Next call after backoff expires retries.
22
+ * - Cross-platform cache dir resolution (Linux/macOS/Windows).
23
+ * - Refuses to load a registration file with non-0600 permissions
24
+ * (defends against silent credential leak via accidental chmod).
25
+ * - Operator opt-out via AXONFLOW_COMMUNITY_SAAS=0 short-circuits the
26
+ * bootstrap entirely; callers see source="opted-out".
27
+ *
28
+ * Environment + filesystem operations live in community-saas-context.ts.
29
+ * This module is the orchestration + network-only side of the bootstrap:
30
+ * it imports plain values from the context module and only issues HTTP
31
+ * requests + invokes the plugin logger.
32
+ */
33
+ export interface BootstrapResult {
34
+ /** Resolved AxonFlow agent endpoint to use for subsequent requests. */
35
+ endpoint: string;
36
+ /** Tenant identity for the AxonFlow client (`cs_<uuid>` for Community SaaS). */
37
+ clientId: string;
38
+ /** Plain-text credential paired with clientId for Basic auth. */
39
+ clientSecret: string;
40
+ /**
41
+ * Source for telemetry / logging:
42
+ * "fresh-registration" — first-time POST to /api/v1/register succeeded.
43
+ * "cached-registration" — existing on-disk credential is fresh enough.
44
+ * "rate-limited" — 429 from the registrar; backoff active.
45
+ * "failed" — network or response error; no credential.
46
+ * "opted-out" — operator set AXONFLOW_COMMUNITY_SAAS=0.
47
+ */
48
+ source: "fresh-registration" | "cached-registration" | "rate-limited" | "failed" | "opted-out";
49
+ }
50
+ /**
51
+ * Optional injection hook for the disclosure banner. The plugin entry
52
+ * point passes its OpenClaw `PluginLogger.warn` here so the banner shows
53
+ * up in plugin/gateway logs in the same style as other plugin warnings.
54
+ * When omitted, falls back to `process.stderr.write` so test harnesses
55
+ * and ad-hoc invocations still surface the disclosure.
56
+ */
57
+ export type DisclosureLogger = (message: string) => void;
58
+ export interface BootstrapOptions {
59
+ registerUrl?: string;
60
+ endpoint?: string;
61
+ pluginVersion?: string;
62
+ fetchImpl?: typeof fetch;
63
+ now?: () => Date;
64
+ /**
65
+ * Caller-provided logger for the first-load Community-SaaS disclosure.
66
+ * Plugin entry passes `api.logger.warn`; tests pass a capture function.
67
+ */
68
+ disclosureLogger?: DisclosureLogger;
69
+ }
70
+ /**
71
+ * Bootstrap a Community-SaaS registration. Returns null if bootstrap was
72
+ * skipped (registration file unreadable due to permissions, network error,
73
+ * 429 rate-limited, etc); the caller is responsible for surfacing a clear
74
+ * "governance degraded" notice in that case.
75
+ *
76
+ * Safe to call concurrently — the second concurrent call awaits the first
77
+ * rather than racing.
78
+ */
79
+ export declare function bootstrapCommunitySaas(opts?: BootstrapOptions): Promise<BootstrapResult | null>;
80
+ /**
81
+ * Test-only: clear the in-flight gate so test cases can exercise concurrent
82
+ * bootstrap calls without sharing state across tests.
83
+ */
84
+ export declare function _resetBootstrapInFlightForTests(): void;
85
+ //# sourceMappingURL=community-saas-bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-saas-bootstrap.d.ts","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AA4BH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,EACF,oBAAoB,GACpB,qBAAqB,GACrB,cAAc,GACd,QAAQ,GACR,WAAW,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AASzD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQjC;AA4MD;;;GAGG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Community-SaaS first-run bootstrap (TypeScript).
3
+ *
4
+ * Mirrors the bash plugins' scripts/community-saas-bootstrap.sh contract:
5
+ * registers the plugin against try.getaxonflow.com on first run when the
6
+ * user has not provided explicit clientId/clientSecret, persists the
7
+ * resulting credential to a 0600 file under the user's config dir, and
8
+ * returns Basic-auth credentials the caller can hand to the AxonFlow client.
9
+ *
10
+ * Design rules:
11
+ * - Stamp-on-delivery: registration file is written ONLY after the
12
+ * POST returns 201 with a valid response body. A network failure
13
+ * leaves the previous (or absent) state untouched.
14
+ * - Atomic writes: temp + rename so a crash mid-write never produces
15
+ * a half-readable file.
16
+ * - In-flight gate: a per-process Promise-based lock so concurrent
17
+ * plugin loads don't both race to register.
18
+ * - File permissions: 0700 directory, 0600 file. The file holds the
19
+ * plain-text credential; world-readable would be a security bug.
20
+ * - 429 (registration rate-limit) → write a short backoff stamp and
21
+ * return null. Next call after backoff expires retries.
22
+ * - Cross-platform cache dir resolution (Linux/macOS/Windows).
23
+ * - Refuses to load a registration file with non-0600 permissions
24
+ * (defends against silent credential leak via accidental chmod).
25
+ * - Operator opt-out via AXONFLOW_COMMUNITY_SAAS=0 short-circuits the
26
+ * bootstrap entirely; callers see source="opted-out".
27
+ *
28
+ * Environment + filesystem operations live in community-saas-context.ts.
29
+ * This module is the orchestration + network-only side of the bootstrap:
30
+ * it imports plain values from the context module and only issues HTTP
31
+ * requests + invokes the plugin logger.
32
+ */
33
+ import * as path from "path";
34
+ import { axonflowCacheDir, axonflowConfigDir } from "./cache-dir.js";
35
+ import { buildRegistrationLabel, disclosureStampPath, ensureSecureDir, hasShownDisclosure, isCommunitySaasOptedOut, isWithinBackoff, markDisclosureShown, readRegistrationIfFreshAndSafe, resolveHarnessInputs, unlinkIfExists, writeFileAtomicallyWithMode, } from "./community-saas-context.js";
36
+ const REGISTER_URL_DEFAULT = "https://try.getaxonflow.com/api/v1/register";
37
+ const ENDPOINT_DEFAULT = "https://try.getaxonflow.com";
38
+ const REGISTRATION_FILE_NAME = "try-registration.json";
39
+ const BACKOFF_FILE_NAME = "openclaw-plugin-register-backoff";
40
+ const BACKOFF_SECONDS = 3600;
41
+ // Refresh registrations whose expires_at is within 30 days so we never let
42
+ // a tenant lapse silently while users are actively using the plugin.
43
+ const REFRESH_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
44
+ /**
45
+ * Per-process in-flight gate. When two plugin loads happen concurrently
46
+ * (rare but possible in test harnesses or hot-reload scenarios), the second
47
+ * waits on the first's promise rather than firing a duplicate registration.
48
+ */
49
+ let inFlight = null;
50
+ /**
51
+ * Bootstrap a Community-SaaS registration. Returns null if bootstrap was
52
+ * skipped (registration file unreadable due to permissions, network error,
53
+ * 429 rate-limited, etc); the caller is responsible for surfacing a clear
54
+ * "governance degraded" notice in that case.
55
+ *
56
+ * Safe to call concurrently — the second concurrent call awaits the first
57
+ * rather than racing.
58
+ */
59
+ export async function bootstrapCommunitySaas(opts) {
60
+ if (inFlight) {
61
+ return inFlight;
62
+ }
63
+ inFlight = bootstrapCommunitySaasInner(opts).finally(() => {
64
+ inFlight = null;
65
+ });
66
+ return inFlight;
67
+ }
68
+ async function bootstrapCommunitySaasInner(opts) {
69
+ // 0. Operator opt-out short-circuits everything. No env-var disclosure
70
+ // lookup, no fs touches, no network — return immediately.
71
+ if (isCommunitySaasOptedOut()) {
72
+ return { endpoint: opts?.endpoint ?? ENDPOINT_DEFAULT, clientId: "", clientSecret: "", source: "opted-out" };
73
+ }
74
+ // 1. Test-harness URL overrides — only honoured when AXONFLOW_HARNESS=1
75
+ // and exclusively used by tests/heartbeat-real-stack/. Production
76
+ // callers leave AXONFLOW_HARNESS unset and the URLs stay pinned to
77
+ // try.getaxonflow.com.
78
+ const harness = resolveHarnessInputs();
79
+ const registerUrl = opts?.registerUrl ?? (harness.harnessRegisterUrl || REGISTER_URL_DEFAULT);
80
+ const endpoint = opts?.endpoint ?? (harness.harnessAgentEndpoint || ENDPOINT_DEFAULT);
81
+ const fetchFn = opts?.fetchImpl ?? fetch;
82
+ const now = opts?.now ?? (() => new Date());
83
+ const configDir = axonflowConfigDir();
84
+ if (!configDir) {
85
+ return null;
86
+ }
87
+ const registrationFile = path.join(configDir, REGISTRATION_FILE_NAME);
88
+ const cacheDir = axonflowCacheDir();
89
+ const backoffFile = cacheDir
90
+ ? path.join(cacheDir, BACKOFF_FILE_NAME)
91
+ : "";
92
+ if (!ensureSecureDir(configDir)) {
93
+ return null;
94
+ }
95
+ // Fast path: existing registration is fresh enough.
96
+ const cached = readRegistrationIfFreshAndSafe(registrationFile, now, REFRESH_WINDOW_MS);
97
+ if (cached) {
98
+ return {
99
+ endpoint: cached.endpoint ?? endpoint,
100
+ clientId: cached.tenant_id,
101
+ clientSecret: cached.secret,
102
+ source: "cached-registration",
103
+ };
104
+ }
105
+ // Backoff path: 429 told us to slow down. Honour it.
106
+ if (backoffFile && isWithinBackoff(backoffFile, now)) {
107
+ return { endpoint, clientId: "", clientSecret: "", source: "rate-limited" };
108
+ }
109
+ // First-load disclosure: announce the auto-registration once per machine
110
+ // before issuing the network call. The stamp is written after the
111
+ // banner emits so we don't re-warn on subsequent loads, but never before
112
+ // the banner emits so a crash mid-disclosure stays loud.
113
+ emitFirstLoadDisclosureIfNeeded({
114
+ configDir,
115
+ endpoint,
116
+ registerUrl,
117
+ logger: opts?.disclosureLogger,
118
+ });
119
+ // Issue the registration.
120
+ const label = buildRegistrationLabel(opts?.pluginVersion);
121
+ let response;
122
+ try {
123
+ const ctl = new AbortController();
124
+ const timeoutHandle = setTimeout(() => ctl.abort(), 10_000);
125
+ try {
126
+ response = await fetchFn(registerUrl, {
127
+ method: "POST",
128
+ headers: { "Content-Type": "application/json" },
129
+ body: JSON.stringify({ label }),
130
+ signal: ctl.signal,
131
+ });
132
+ }
133
+ finally {
134
+ clearTimeout(timeoutHandle);
135
+ }
136
+ }
137
+ catch {
138
+ return { endpoint, clientId: "", clientSecret: "", source: "failed" };
139
+ }
140
+ if (response.status === 429) {
141
+ if (backoffFile && cacheDir && ensureSecureDir(cacheDir)) {
142
+ try {
143
+ const backoffUntil = Math.floor(now().getTime() / 1000) + BACKOFF_SECONDS;
144
+ writeFileAtomicallyWithMode(backoffFile, String(backoffUntil), 0o600);
145
+ }
146
+ catch {
147
+ // Best effort; if we can't write the backoff stamp, the next call retries.
148
+ }
149
+ }
150
+ return { endpoint, clientId: "", clientSecret: "", source: "rate-limited" };
151
+ }
152
+ if (response.status !== 201) {
153
+ return { endpoint, clientId: "", clientSecret: "", source: "failed" };
154
+ }
155
+ let parsed;
156
+ try {
157
+ const body = (await response.json());
158
+ if (typeof body.tenant_id !== "string" || body.tenant_id.length === 0 ||
159
+ typeof body.secret !== "string" || body.secret.length === 0 ||
160
+ typeof body.expires_at !== "string") {
161
+ return { endpoint, clientId: "", clientSecret: "", source: "failed" };
162
+ }
163
+ parsed = {
164
+ tenant_id: body.tenant_id,
165
+ secret: body.secret,
166
+ expires_at: body.expires_at,
167
+ endpoint: typeof body.endpoint === "string" ? body.endpoint : endpoint,
168
+ };
169
+ }
170
+ catch {
171
+ return { endpoint, clientId: "", clientSecret: "", source: "failed" };
172
+ }
173
+ // Stamp-on-delivery: only write after a fully-validated response.
174
+ try {
175
+ writeFileAtomicallyWithMode(registrationFile, JSON.stringify(parsed), 0o600);
176
+ if (backoffFile) {
177
+ unlinkIfExists(backoffFile);
178
+ }
179
+ }
180
+ catch {
181
+ // We received valid credentials but couldn't persist them. Return them
182
+ // anyway so the current process can still authenticate; on next run we
183
+ // re-register (cheap; rate-limited, but bounded).
184
+ }
185
+ return {
186
+ endpoint: parsed.endpoint ?? endpoint,
187
+ clientId: parsed.tenant_id,
188
+ clientSecret: parsed.secret,
189
+ source: "fresh-registration",
190
+ };
191
+ }
192
+ function emitFirstLoadDisclosureIfNeeded(inputs) {
193
+ const stampFile = disclosureStampPath(inputs.configDir);
194
+ if (hasShownDisclosure(stampFile)) {
195
+ return;
196
+ }
197
+ const banner = buildDisclosureBanner(inputs.endpoint, inputs.registerUrl);
198
+ const delivered = emitDisclosureBanner(banner, inputs.logger);
199
+ if (delivered) {
200
+ markDisclosureShown(stampFile);
201
+ }
202
+ // If neither logger nor stderr accepted the banner, leave the stamp
203
+ // unwritten so the next load tries again. Better to re-warn once we have
204
+ // a working output than to silently swallow the disclosure.
205
+ }
206
+ function buildDisclosureBanner(endpoint, registerUrl) {
207
+ const url = new URL(registerUrl);
208
+ const host = url.host;
209
+ return [
210
+ "AxonFlow Governance — Community SaaS auto-registration",
211
+ "",
212
+ ` This plugin will register with ${host} (Community SaaS) and use it`,
213
+ " to evaluate tool inputs and message bodies for policy + audit.",
214
+ "",
215
+ " What is sent off-host on each governed call:",
216
+ " - tool name + arguments before execution",
217
+ " - outbound message bodies before delivery",
218
+ " What is NOT sent: LLM provider keys, OpenClaw conversation history",
219
+ " outside governed tools, or any data outside the OpenClaw runtime.",
220
+ "",
221
+ " To opt out: set AXONFLOW_COMMUNITY_SAAS=0 in your environment, or",
222
+ " point the plugin at your own AxonFlow instance:",
223
+ " pluginConfig.endpoint = \"https://your-axonflow.example.com\"",
224
+ "",
225
+ " This message shows once per machine; remove the disclosure stamp",
226
+ ` to re-display: rm "$AXONFLOW_CONFIG_DIR"/openclaw-plugin-community-saas-disclosure-shown`,
227
+ ` Default endpoint: ${endpoint}`,
228
+ " Docs: https://docs.getaxonflow.com/docs/integration/openclaw/",
229
+ ].join("\n");
230
+ }
231
+ function emitDisclosureBanner(banner, logger) {
232
+ if (logger) {
233
+ try {
234
+ logger(banner);
235
+ return true;
236
+ }
237
+ catch {
238
+ // Fall through to stderr.
239
+ }
240
+ }
241
+ try {
242
+ process.stderr.write(banner + "\n");
243
+ return true;
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ }
249
+ /**
250
+ * Test-only: clear the in-flight gate so test cases can exercise concurrent
251
+ * bootstrap calls without sharing state across tests.
252
+ */
253
+ export function _resetBootstrapInFlightForTests() {
254
+ inFlight = null;
255
+ }
256
+ //# sourceMappingURL=community-saas-bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-saas-bootstrap.js","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Community-SaaS bootstrap context — environment + filesystem operations.
3
+ *
4
+ * Mirrors the split applied to the heartbeat path: env access and fs
5
+ * reads/writes live here, away from the network-sending side
6
+ * (community-saas-bootstrap.ts). Static-analysis heuristics that flag
7
+ * env-or-fs-read co-located with outbound HTTP therefore do not trip on
8
+ * the compiled bootstrap module.
9
+ *
10
+ * Pure-data module: callers receive plain values and pass them into the
11
+ * orchestration layer. No outbound HTTP lives here.
12
+ */
13
+ /**
14
+ * Test-harness inputs read from process.env. Only honoured when
15
+ * AXONFLOW_HARNESS=1 — production callers leave the var unset and the
16
+ * defaults pin to try.getaxonflow.com.
17
+ */
18
+ export interface HarnessInputs {
19
+ harnessOn: boolean;
20
+ harnessRegisterUrl: string;
21
+ harnessAgentEndpoint: string;
22
+ }
23
+ export declare function resolveHarnessInputs(): HarnessInputs;
24
+ /**
25
+ * Operator opt-out for Community-SaaS auto-bootstrap. Honours
26
+ * AXONFLOW_COMMUNITY_SAAS = "0" | "false" | "off" | "no"
27
+ * (case-insensitive). Any other value (including unset) leaves the default
28
+ * auto-bootstrap behaviour unchanged.
29
+ *
30
+ * This is the only programmatic way an operator can disable the implicit
31
+ * try.getaxonflow.com registration without supplying a self-hosted
32
+ * endpoint. Documented in README and surfaced in the first-load disclosure
33
+ * banner so the consent surface is real.
34
+ */
35
+ export declare function isCommunitySaasOptedOut(): boolean;
36
+ /**
37
+ * Ensure a directory exists with mode 0o700 (owner-only on POSIX). Returns
38
+ * true on success or false on any failure so callers can degrade safely.
39
+ */
40
+ export declare function ensureSecureDir(dir: string): boolean;
41
+ export interface PersistedRegistration {
42
+ tenant_id: string;
43
+ secret: string;
44
+ expires_at: string;
45
+ endpoint?: string;
46
+ }
47
+ /**
48
+ * Read a Community-SaaS registration file if it is fresh enough to use,
49
+ * has well-formed contents, and (on POSIX) lives at mode 0o600. Returns
50
+ * null when the file is missing, malformed, expired, or unsafe.
51
+ *
52
+ * `refreshWindowMs` lets the caller decide how aggressively to refresh —
53
+ * passing the same window ensures cached + fresh paths agree on lifetime.
54
+ */
55
+ export declare function readRegistrationIfFreshAndSafe(file: string, now: () => Date, refreshWindowMs: number): PersistedRegistration | null;
56
+ export declare function isWithinBackoff(backoffFile: string, now: () => Date): boolean;
57
+ /**
58
+ * Atomic file write (tmp + rename) with an explicit POSIX mode. Used for
59
+ * the registration file (0o600) and the rate-limit backoff stamp (0o600).
60
+ */
61
+ export declare function writeFileAtomicallyWithMode(file: string, content: string, mode: number): void;
62
+ export declare function unlinkIfExists(file: string): void;
63
+ /**
64
+ * Build the registration label sent in the POST body. Pure stdlib — no env
65
+ * reads, no fs reads — kept here so the bootstrap module stays free of any
66
+ * `os.*` calls that might confuse future scanner heuristics.
67
+ */
68
+ export declare function buildRegistrationLabel(pluginVersion: string | undefined): string;
69
+ /**
70
+ * First-load disclosure stamp helpers. The bootstrap path emits a one-time
71
+ * warning to the plugin logger explaining that auto-Community-SaaS
72
+ * registration is about to happen and how to opt out. The stamp keeps the
73
+ * warning from re-firing on every plugin reload.
74
+ *
75
+ * Stamp file lives next to the registration file so it shares the same
76
+ * config-dir lifecycle (rm of try-registration.json without a re-warn is
77
+ * intentional; the user already knows we register).
78
+ */
79
+ export declare function disclosureStampPath(configDir: string): string;
80
+ export declare function hasShownDisclosure(stampFile: string): boolean;
81
+ export declare function markDisclosureShown(stampFile: string): void;
82
+ //# sourceMappingURL=community-saas-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-saas-context.d.ts","sourceRoot":"","sources":["../src/community-saas-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,oBAAoB,IAAI,aAAa,CAKpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAKjD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWpD;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,IAAI,EACf,eAAe,EAAE,MAAM,GACtB,qBAAqB,GAAG,IAAI,CA0C9B;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAU7E;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM7F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGjD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAQ7D;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAQ3D"}