@axonflow/openclaw 2.0.0 → 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.
- package/CHANGELOG.md +26 -0
- package/README.md +55 -1
- package/dist/community-saas-bootstrap.d.ts +38 -11
- package/dist/community-saas-bootstrap.d.ts.map +1 -1
- package/dist/community-saas-bootstrap.js +83 -108
- package/dist/community-saas-bootstrap.js.map +1 -1
- package/dist/community-saas-context.d.ts +82 -0
- package/dist/community-saas-context.d.ts.map +1 -0
- package/dist/community-saas-context.js +196 -0
- package/dist/community-saas-context.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -54
- package/dist/index.js.map +1 -1
- package/dist/telemetry-context.d.ts +65 -0
- package/dist/telemetry-context.d.ts.map +1 -0
- package/dist/telemetry-context.js +116 -0
- package/dist/telemetry-context.js.map +1 -0
- package/dist/telemetry.d.ts +6 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +30 -68
- package/dist/telemetry.js.map +1 -1
- package/openclaw.plugin.json +57 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.1] - 2026-04-30 — Restore ClawHub install + explicit Community-SaaS consent surface
|
|
4
|
+
|
|
5
|
+
ClawHub's static-analysis scanner blocked install of `@axonflow/openclaw@2.0.0` because the telemetry and Community-SaaS bootstrap modules co-located `process.env.*` access and `fs.readFileSync(...)` calls with the outbound `fetch(...)` in the same compiled file — a pattern the scanner heuristically flags as credential-harvesting / potential data exfiltration. This release restores a clean install path on every supported OpenClaw host, adds a real opt-out for Community-SaaS auto-registration, and ships a CI gate so this class of regression cannot recur.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`AXONFLOW_COMMUNITY_SAAS=0` opt-out** for the default Community-SaaS auto-registration. When set (also accepts `false`, `off`, `no`), the plugin loads but does not POST to `try.getaxonflow.com/api/v1/register` and does not write `try-registration.json`. Operators who want explicit control over outbound traffic — air-gapped labs, regulated networks — can now turn the auto-bootstrap off without removing the plugin.
|
|
10
|
+
- **First-load Community-SaaS consent disclosure banner.** Before the registration POST fires, the plugin emits a warn-level log line via the OpenClaw plugin logger listing exactly what gets sent off-host (tool name + arguments, outbound message bodies), what does not (LLM provider keys, conversation history outside governed tools), and how to opt out. Banner shows once per machine; presence of the disclosure stamp prevents re-warning on subsequent loads.
|
|
11
|
+
- **Pre-publish security scan gate.** `npm run scan` packs the plugin, extracts the tarball into an isolated state directory, and runs the official OpenClaw scanner against the exact artifact ClawHub re-scans at publish time. A new `.github/workflows/security-scan.yml` runs the same script PR-blocking on every change to `src/`, `dist/`, `package.json`, `openclaw.plugin.json`. Catches scanner regressions before they ship instead of after.
|
|
12
|
+
- **`envVars` and `runtimeBehavior` declarations** in `openclaw.plugin.json`. Documents the four user-facing environment variables (`AXONFLOW_TELEMETRY`, `AXONFLOW_COMMUNITY_SAAS`, `AXONFLOW_CACHE_DIR`, `AXONFLOW_CONFIG_DIR`), the auto-bootstrap data flow, the four persisted files and their permission modes. Registry metadata now matches what the code actually does.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Telemetry module split into `telemetry.ts` + `telemetry-context.ts`.** Environment reads (harness override) and stamp-file reads/writes live in the context module; the network-sending module imports plain values. Behaviour is identical to v2.0.0; only the on-disk module boundary moved. Same change applied to `community-saas-bootstrap.ts` + new `community-saas-context.ts`.
|
|
17
|
+
- **`README.md` rewritten** around a new **"Where your data goes"** section. Replaces the previous data-locality paragraph (which was accurate before v2.0.0 made Community SaaS the default but became misleading after) with three explicit deployment modes: default Community SaaS (what's sent off-host, link to the trial-server disclosure page), self-hosted (your own AxonFlow), and air-gapped (`AXONFLOW_COMMUNITY_SAAS=0` + `AXONFLOW_TELEMETRY=off` = zero outbound). Cross-links the [Try AxonFlow — Free Trial Server](https://docs.getaxonflow.com/docs/deployment/community-saas/) docs page so users can read the full Community SaaS terms.
|
|
18
|
+
- **Removed** the legacy `showCommunitySaasDisclosureOnce` info-level banner that fired *after* the connection was established. The new warn-level banner fires *before* the registration POST, with explicit data-flow disclosure and opt-out instructions, so the consent surface is real rather than after-the-fact.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **`clawhub:@axonflow/openclaw` install no longer blocked** by the host static-analysis scanner on OpenClaw `>=2026.4.15`. Verified with the local `openclaw plugins install` against the packed tarball: scanner reports `0 criticals, 0 warnings`.
|
|
23
|
+
|
|
24
|
+
### Security
|
|
25
|
+
|
|
26
|
+
- The OpenClaw `>=2026.4.15` peer floor remains in place — it is a real CVE floor (Feishu webhook + card-action validation fail-open in OpenClaw `<2026.4.15`, [GHSA-xh72-v6v9-mwhc](https://github.com/getaxonflow/axonflow-openclaw-plugin/security/advisories/GHSA-cqmh-pcgr-q42f)) and is not relaxed by this release. Anyone running an older OpenClaw should upgrade their host.
|
|
27
|
+
|
|
28
|
+
|
|
3
29
|
## [2.0.0] - 2026-04-29 — Production, quality, and security hardening — upgrade encouraged
|
|
4
30
|
|
|
5
31
|
**Upgrade strongly recommended.** Over the past month we've shipped substantial production, quality, and security hardening across the AxonFlow plugin and platform — upgrade to the latest version for a more secure, reliable, and bug-free experience.
|
package/README.md
CHANGED
|
@@ -23,7 +23,61 @@ OpenClaw is a strong agent runtime. It is also a serious production security pro
|
|
|
23
23
|
|
|
24
24
|
OpenClaw handles agent runtime, MCP connectivity, channels, and tool execution. It was never intended to be the place you enforce governance. This plugin adds the governance layer on top, so OpenClaw keeps doing what it does well and AxonFlow takes over the "is this allowed, should this redact, who approved, where is the audit record" questions.
|
|
25
25
|
|
|
26
|
-
**AxonFlow governs. OpenClaw orchestrates
|
|
26
|
+
**AxonFlow governs. OpenClaw orchestrates.** OpenClaw still makes every LLM call; AxonFlow only evaluates policies and records audit trails. LLM provider keys never leave your machine. Where the rest of your data goes — tool inputs, message bodies — depends on which deployment mode you pick. See **[Where your data goes](#where-your-data-goes)** below.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Where your data goes
|
|
31
|
+
|
|
32
|
+
The plugin governs tool calls and outbound messages by sending each one to an AxonFlow endpoint for policy evaluation + audit. The endpoint can be the AxonFlow Community SaaS (default, zero-config), a self-hosted AxonFlow instance you run, or nothing at all if you opt out.
|
|
33
|
+
|
|
34
|
+
### Default: AxonFlow Community SaaS
|
|
35
|
+
|
|
36
|
+
If you install the plugin without setting `pluginConfig.endpoint`, it auto-registers with **`try.getaxonflow.com`** (Community SaaS) on first load and sends governed traffic there. The first-load disclosure banner surfaces this in your plugin logs before the registration POST fires.
|
|
37
|
+
|
|
38
|
+
| What goes to `try.getaxonflow.com` | What does NOT |
|
|
39
|
+
|---|---|
|
|
40
|
+
| Tool name + arguments before each governed call | LLM provider API keys |
|
|
41
|
+
| Outbound message bodies before delivery (PII/secret scan) | OpenClaw conversation history outside governed tools |
|
|
42
|
+
| Anonymous 7-day heartbeat (plugin version, OS, runtime) | Files outside the OpenClaw runtime |
|
|
43
|
+
|
|
44
|
+
Community SaaS is intended for evaluation and prototyping. It has no SLA, no production guarantees, runs against shared Ollama models, and rate-limits at 20 req/min · 500 req/day per tenant. Read the [Try AxonFlow — Free Trial Server](https://docs.getaxonflow.com/docs/deployment/community-saas/) page for the full disclosure, including [data retention](https://docs.getaxonflow.com/docs/deployment/community-saas/#limitations-and-disclaimers) and [registration mechanics](https://docs.getaxonflow.com/docs/deployment/community-saas/#registration).
|
|
45
|
+
|
|
46
|
+
Auto-registration credentials persist at `$AXONFLOW_CONFIG_DIR/try-registration.json` (mode `0600`).
|
|
47
|
+
|
|
48
|
+
### Self-hosted: your own AxonFlow
|
|
49
|
+
|
|
50
|
+
Point the plugin at an AxonFlow instance you run. Nothing leaves your network except the anonymous 7-day heartbeat.
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
plugins:
|
|
54
|
+
axonflow-governance:
|
|
55
|
+
endpoint: https://axonflow.your-corp.example.com
|
|
56
|
+
clientId: your-client-id
|
|
57
|
+
clientSecret: your-secret
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This is the recommended setup for any real workflow, real systems, or sensitive data. See [Getting Started](https://docs.getaxonflow.com/docs/getting-started/) for deployment options or the [OpenClaw integration guide](https://docs.getaxonflow.com/docs/integration/openclaw/) for the architecture.
|
|
61
|
+
|
|
62
|
+
### Air-gapped: zero outbound
|
|
63
|
+
|
|
64
|
+
If you need the plugin to make *no* outbound calls at all — air-gapped lab, regulated network, etc. — set both:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export AXONFLOW_COMMUNITY_SAAS=0 # disable auto-bootstrap
|
|
68
|
+
export AXONFLOW_TELEMETRY=off # disable 7-day heartbeat
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
…and configure `pluginConfig.endpoint` to a self-hosted AxonFlow on the same network. With these set, no traffic leaves your environment.
|
|
72
|
+
|
|
73
|
+
### Environment variables (optional)
|
|
74
|
+
|
|
75
|
+
| Variable | Effect |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `AXONFLOW_TELEMETRY=off` | Disables the 7-day anonymous heartbeat to `checkpoint.getaxonflow.com`. Accepted off-values: `off`, `0`, `false`, `no`. |
|
|
78
|
+
| `AXONFLOW_COMMUNITY_SAAS=0` | Disables auto-registration with `try.getaxonflow.com`. You must then set `pluginConfig.endpoint` for the plugin to enforce policy. Accepted off-values: `0`, `false`, `off`, `no`. |
|
|
79
|
+
| `AXONFLOW_CACHE_DIR` | Overrides the per-user cache dir (telemetry stamp, rate-limit backoff). Defaults to `$XDG_CACHE_HOME/axonflow` (Linux), `~/Library/Caches/axonflow` (macOS), `%LOCALAPPDATA%\axonflow` (Windows). |
|
|
80
|
+
| `AXONFLOW_CONFIG_DIR` | Overrides the per-user config dir (Community-SaaS registration file, disclosure stamp). Defaults to OS conventions. |
|
|
27
81
|
|
|
28
82
|
---
|
|
29
83
|
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* resulting credential to a 0600 file under the user's config dir, and
|
|
8
8
|
* returns Basic-auth credentials the caller can hand to the AxonFlow client.
|
|
9
9
|
*
|
|
10
|
-
* Design rules
|
|
11
|
-
* feedback_pg_advisory_lock_pin_connection.md):
|
|
10
|
+
* Design rules:
|
|
12
11
|
* - Stamp-on-delivery: registration file is written ONLY after the
|
|
13
12
|
* POST returns 201 with a valid response body. A network failure
|
|
14
13
|
* leaves the previous (or absent) state untouched.
|
|
@@ -23,6 +22,13 @@
|
|
|
23
22
|
* - Cross-platform cache dir resolution (Linux/macOS/Windows).
|
|
24
23
|
* - Refuses to load a registration file with non-0600 permissions
|
|
25
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.
|
|
26
32
|
*/
|
|
27
33
|
export interface BootstrapResult {
|
|
28
34
|
/** Resolved AxonFlow agent endpoint to use for subsequent requests. */
|
|
@@ -31,8 +37,35 @@ export interface BootstrapResult {
|
|
|
31
37
|
clientId: string;
|
|
32
38
|
/** Plain-text credential paired with clientId for Basic auth. */
|
|
33
39
|
clientSecret: string;
|
|
34
|
-
/**
|
|
35
|
-
|
|
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;
|
|
36
69
|
}
|
|
37
70
|
/**
|
|
38
71
|
* Bootstrap a Community-SaaS registration. Returns null if bootstrap was
|
|
@@ -43,13 +76,7 @@ export interface BootstrapResult {
|
|
|
43
76
|
* Safe to call concurrently — the second concurrent call awaits the first
|
|
44
77
|
* rather than racing.
|
|
45
78
|
*/
|
|
46
|
-
export declare function bootstrapCommunitySaas(opts?:
|
|
47
|
-
registerUrl?: string;
|
|
48
|
-
endpoint?: string;
|
|
49
|
-
pluginVersion?: string;
|
|
50
|
-
fetchImpl?: typeof fetch;
|
|
51
|
-
now?: () => Date;
|
|
52
|
-
}): Promise<BootstrapResult | null>;
|
|
79
|
+
export declare function bootstrapCommunitySaas(opts?: BootstrapOptions): Promise<BootstrapResult | null>;
|
|
53
80
|
/**
|
|
54
81
|
* Test-only: clear the in-flight gate so test cases can exercise concurrent
|
|
55
82
|
* bootstrap calls without sharing state across tests.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-saas-bootstrap.d.ts","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* resulting credential to a 0600 file under the user's config dir, and
|
|
8
8
|
* returns Basic-auth credentials the caller can hand to the AxonFlow client.
|
|
9
9
|
*
|
|
10
|
-
* Design rules
|
|
11
|
-
* feedback_pg_advisory_lock_pin_connection.md):
|
|
10
|
+
* Design rules:
|
|
12
11
|
* - Stamp-on-delivery: registration file is written ONLY after the
|
|
13
12
|
* POST returns 201 with a valid response body. A network failure
|
|
14
13
|
* leaves the previous (or absent) state untouched.
|
|
@@ -23,11 +22,17 @@
|
|
|
23
22
|
* - Cross-platform cache dir resolution (Linux/macOS/Windows).
|
|
24
23
|
* - Refuses to load a registration file with non-0600 permissions
|
|
25
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.
|
|
26
32
|
*/
|
|
27
|
-
import * as fs from "fs";
|
|
28
|
-
import * as os from "os";
|
|
29
33
|
import * as path from "path";
|
|
30
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";
|
|
31
36
|
const REGISTER_URL_DEFAULT = "https://try.getaxonflow.com/api/v1/register";
|
|
32
37
|
const ENDPOINT_DEFAULT = "https://try.getaxonflow.com";
|
|
33
38
|
const REGISTRATION_FILE_NAME = "try-registration.json";
|
|
@@ -61,15 +66,18 @@ export async function bootstrapCommunitySaas(opts) {
|
|
|
61
66
|
return inFlight;
|
|
62
67
|
}
|
|
63
68
|
async function bootstrapCommunitySaasInner(opts) {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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);
|
|
73
81
|
const fetchFn = opts?.fetchImpl ?? fetch;
|
|
74
82
|
const now = opts?.now ?? (() => new Date());
|
|
75
83
|
const configDir = axonflowConfigDir();
|
|
@@ -81,24 +89,11 @@ async function bootstrapCommunitySaasInner(opts) {
|
|
|
81
89
|
const backoffFile = cacheDir
|
|
82
90
|
? path.join(cacheDir, BACKOFF_FILE_NAME)
|
|
83
91
|
: "";
|
|
84
|
-
|
|
85
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
86
|
-
// mkdir's mode only applies to directories it creates. A user with
|
|
87
|
-
// ~/.config already at 0755 would otherwise hold our 0600 credential
|
|
88
|
-
// file inside a traversable directory; chmod restores the dir-mode
|
|
89
|
-
// contract on every invocation. Skip on Windows (mode bits don't map).
|
|
90
|
-
if (process.platform !== "win32") {
|
|
91
|
-
try {
|
|
92
|
-
fs.chmodSync(configDir, 0o700);
|
|
93
|
-
}
|
|
94
|
-
catch { /* best effort */ }
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
92
|
+
if (!ensureSecureDir(configDir)) {
|
|
98
93
|
return null;
|
|
99
94
|
}
|
|
100
95
|
// Fast path: existing registration is fresh enough.
|
|
101
|
-
const cached = readRegistrationIfFreshAndSafe(registrationFile, now);
|
|
96
|
+
const cached = readRegistrationIfFreshAndSafe(registrationFile, now, REFRESH_WINDOW_MS);
|
|
102
97
|
if (cached) {
|
|
103
98
|
return {
|
|
104
99
|
endpoint: cached.endpoint ?? endpoint,
|
|
@@ -111,8 +106,18 @@ async function bootstrapCommunitySaasInner(opts) {
|
|
|
111
106
|
if (backoffFile && isWithinBackoff(backoffFile, now)) {
|
|
112
107
|
return { endpoint, clientId: "", clientSecret: "", source: "rate-limited" };
|
|
113
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
|
+
});
|
|
114
119
|
// Issue the registration.
|
|
115
|
-
const label =
|
|
120
|
+
const label = buildRegistrationLabel(opts?.pluginVersion);
|
|
116
121
|
let response;
|
|
117
122
|
try {
|
|
118
123
|
const ctl = new AbortController();
|
|
@@ -133,15 +138,8 @@ async function bootstrapCommunitySaasInner(opts) {
|
|
|
133
138
|
return { endpoint, clientId: "", clientSecret: "", source: "failed" };
|
|
134
139
|
}
|
|
135
140
|
if (response.status === 429) {
|
|
136
|
-
if (backoffFile && cacheDir) {
|
|
141
|
+
if (backoffFile && cacheDir && ensureSecureDir(cacheDir)) {
|
|
137
142
|
try {
|
|
138
|
-
fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 });
|
|
139
|
-
if (process.platform !== "win32") {
|
|
140
|
-
try {
|
|
141
|
-
fs.chmodSync(cacheDir, 0o700);
|
|
142
|
-
}
|
|
143
|
-
catch { /* best effort */ }
|
|
144
|
-
}
|
|
145
143
|
const backoffUntil = Math.floor(now().getTime() / 1000) + BACKOFF_SECONDS;
|
|
146
144
|
writeFileAtomicallyWithMode(backoffFile, String(backoffUntil), 0o600);
|
|
147
145
|
}
|
|
@@ -176,10 +174,7 @@ async function bootstrapCommunitySaasInner(opts) {
|
|
|
176
174
|
try {
|
|
177
175
|
writeFileAtomicallyWithMode(registrationFile, JSON.stringify(parsed), 0o600);
|
|
178
176
|
if (backoffFile) {
|
|
179
|
-
|
|
180
|
-
fs.unlinkSync(backoffFile);
|
|
181
|
-
}
|
|
182
|
-
catch { /* fine */ }
|
|
177
|
+
unlinkIfExists(backoffFile);
|
|
183
178
|
}
|
|
184
179
|
}
|
|
185
180
|
catch {
|
|
@@ -194,83 +189,63 @@ async function bootstrapCommunitySaasInner(opts) {
|
|
|
194
189
|
source: "fresh-registration",
|
|
195
190
|
};
|
|
196
191
|
}
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
192
|
+
function emitFirstLoadDisclosureIfNeeded(inputs) {
|
|
193
|
+
const stampFile = disclosureStampPath(inputs.configDir);
|
|
194
|
+
if (hasShownDisclosure(stampFile)) {
|
|
195
|
+
return;
|
|
201
196
|
}
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
const banner = buildDisclosureBanner(inputs.endpoint, inputs.registerUrl);
|
|
198
|
+
const delivered = emitDisclosureBanner(banner, inputs.logger);
|
|
199
|
+
if (delivered) {
|
|
200
|
+
markDisclosureShown(stampFile);
|
|
204
201
|
}
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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.
|
|
217
239
|
}
|
|
218
240
|
}
|
|
219
|
-
let parsed;
|
|
220
|
-
try {
|
|
221
|
-
const raw = fs.readFileSync(file, "utf8");
|
|
222
|
-
parsed = JSON.parse(raw);
|
|
223
|
-
}
|
|
224
|
-
catch {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
if (typeof parsed.tenant_id !== "string" || parsed.tenant_id.length === 0 ||
|
|
228
|
-
typeof parsed.secret !== "string" || parsed.secret.length === 0 ||
|
|
229
|
-
typeof parsed.expires_at !== "string") {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
const expiresMs = Date.parse(parsed.expires_at);
|
|
233
|
-
if (!Number.isFinite(expiresMs)) {
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
const remaining = expiresMs - now().getTime();
|
|
237
|
-
if (remaining < REFRESH_WINDOW_MS) {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
return parsed;
|
|
241
|
-
}
|
|
242
|
-
function isWithinBackoff(backoffFile, now) {
|
|
243
241
|
try {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (!Number.isFinite(until) || until <= 0)
|
|
247
|
-
return false;
|
|
248
|
-
return until > Math.floor(now().getTime() / 1000);
|
|
242
|
+
process.stderr.write(banner + "\n");
|
|
243
|
+
return true;
|
|
249
244
|
}
|
|
250
245
|
catch {
|
|
251
246
|
return false;
|
|
252
247
|
}
|
|
253
248
|
}
|
|
254
|
-
function buildLabel(pluginVersion) {
|
|
255
|
-
const version = pluginVersion ?? "unknown";
|
|
256
|
-
const platform = `${os.type()}-${os.arch()}`;
|
|
257
|
-
const label = `openclaw-plugin@${version} / ${platform}`;
|
|
258
|
-
return label.length > 255 ? label.slice(0, 255) : label;
|
|
259
|
-
}
|
|
260
|
-
function writeFileAtomicallyWithMode(file, content, mode) {
|
|
261
|
-
// tmp file in the same directory so rename is atomic on POSIX. On Windows,
|
|
262
|
-
// fs.renameSync replaces the destination atomically since Node 14+.
|
|
263
|
-
const dir = path.dirname(file);
|
|
264
|
-
const tmp = path.join(dir, `${path.basename(file)}.tmp.${process.pid}`);
|
|
265
|
-
fs.writeFileSync(tmp, content, { mode });
|
|
266
|
-
// chmod again because some filesystems / umask combinations ignore the
|
|
267
|
-
// mode passed to writeFileSync for already-existing temp files.
|
|
268
|
-
try {
|
|
269
|
-
fs.chmodSync(tmp, mode);
|
|
270
|
-
}
|
|
271
|
-
catch { /* best effort */ }
|
|
272
|
-
fs.renameSync(tmp, file);
|
|
273
|
-
}
|
|
274
249
|
/**
|
|
275
250
|
* Test-only: clear the in-flight gate so test cases can exercise concurrent
|
|
276
251
|
* bootstrap calls without sharing state across tests.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-saas-bootstrap.js","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"community-saas-bootstrap.js","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,mBAAmB,EACnB,8BAA8B,EAC9B,oBAAoB,EACpB,cAAc,EACd,2BAA2B,GAE5B,MAAM,6BAA6B,CAAC;AAErC,MAAM,oBAAoB,GAAG,6CAA6C,CAAC;AAC3E,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AACvD,MAAM,sBAAsB,GAAG,uBAAuB,CAAC;AACvD,MAAM,iBAAiB,GAAG,kCAAkC,CAAC;AAC7D,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,2EAA2E;AAC3E,qEAAqE;AACrE,MAAM,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAkCnD;;;;GAIG;AACH,IAAI,QAAQ,GAA2C,IAAI,CAAC;AAe5D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAuB;IAEvB,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,QAAQ,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;QACxD,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,KAAK,UAAU,2BAA2B,CACxC,IAAuB;IAEvB,uEAAuE;IACvE,6DAA6D;IAC7D,IAAI,uBAAuB,EAAE,EAAE,CAAC;QAC9B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,IAAI,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC/G,CAAC;IAED,wEAAwE;IACxE,qEAAqE;IACrE,sEAAsE;IACtE,0BAA0B;IAC1B,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;IACvC,MAAM,WAAW,GAAG,IAAI,EAAE,WAAW,IAAI,CAAC,OAAO,CAAC,kBAAkB,IAAI,oBAAoB,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC,oBAAoB,IAAI,gBAAgB,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,IAAI,EAAE,SAAS,IAAI,KAAK,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IACtC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAEtE,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,QAAQ;QAC1B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC;QACxC,CAAC,CAAC,EAAE,CAAC;IAEP,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oDAAoD;IACpD,MAAM,MAAM,GAAG,8BAA8B,CAAC,gBAAgB,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;IACxF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,QAAQ;YACrC,QAAQ,EAAE,MAAM,CAAC,SAAS;YAC1B,YAAY,EAAE,MAAM,CAAC,MAAM;YAC3B,MAAM,EAAE,qBAAqB;SAC9B,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,IAAI,WAAW,IAAI,eAAe,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC9E,CAAC;IAED,yEAAyE;IACzE,kEAAkE;IAClE,yEAAyE;IACzE,yDAAyD;IACzD,+BAA+B,CAAC;QAC9B,SAAS;QACT,QAAQ;QACR,WAAW;QACX,MAAM,EAAE,IAAI,EAAE,gBAAgB;KAC/B,CAAC,CAAC;IAEH,0BAA0B;IAC1B,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC1D,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE;gBACpC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;gBAC/B,MAAM,EAAE,GAAG,CAAC,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxE,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,IAAI,WAAW,IAAI,QAAQ,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,eAAe,CAAC;gBAC1E,2BAA2B,CAAC,WAAW,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;YACxE,CAAC;YAAC,MAAM,CAAC;gBACP,2EAA2E;YAC7E,CAAC;QACH,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC9E,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxE,CAAC;IAED,IAAI,MAA6B,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmC,CAAC;QACvE,IACE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;YACjE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAC3D,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,EACnC,CAAC;YACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACxE,CAAC;QACD,MAAM,GAAG;YACP,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ;SACvE,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxE,CAAC;IAED,kEAAkE;IAClE,IAAI,CAAC;QACH,2BAA2B,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAC7E,IAAI,WAAW,EAAE,CAAC;YAChB,cAAc,CAAC,WAAW,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,uEAAuE;QACvE,kDAAkD;IACpD,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,QAAQ;QACrC,QAAQ,EAAE,MAAM,CAAC,SAAS;QAC1B,YAAY,EAAE,MAAM,CAAC,MAAM;QAC3B,MAAM,EAAE,oBAAoB;KAC7B,CAAC;AACJ,CAAC;AASD,SAAS,+BAA+B,CAAC,MAA4B;IACnE,MAAM,SAAS,GAAG,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9D,IAAI,SAAS,EAAE,CAAC;QACd,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IACD,oEAAoE;IACpE,yEAAyE;IACzE,4DAA4D;AAC9D,CAAC;AAED,SAAS,qBAAqB,CAAC,QAAgB,EAAE,WAAmB;IAClE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,OAAO;QACL,wDAAwD;QACxD,EAAE;QACF,oCAAoC,IAAI,8BAA8B;QACtE,kEAAkE;QAClE,EAAE;QACF,gDAAgD;QAChD,8CAA8C;QAC9C,+CAA+C;QAC/C,sEAAsE;QACtE,qEAAqE;QACrE,EAAE;QACF,qEAAqE;QACrE,mDAAmD;QACnD,qEAAqE;QACrE,EAAE;QACF,oEAAoE;QACpE,4FAA4F;QAC5F,uBAAuB,QAAQ,EAAE;QACjC,iEAAiE;KAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAc,EAAE,MAAoC;IAChF,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B;IAC7C,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -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"}
|