@cernion/openclaw-energy-tools-sidecar 0.1.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 +12 -0
- package/CONTRIBUTING.md +31 -0
- package/LICENSE +201 -0
- package/README.md +202 -0
- package/SECURITY.md +27 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +1053 -0
- package/docker/.env.example +40 -0
- package/docker/Dockerfile +36 -0
- package/docker/README.md +302 -0
- package/docker/compose.sidecar-it.yml +49 -0
- package/docker/compose.yml +29 -0
- package/docker/entrypoint.sh +249 -0
- package/docker/profiles/cernion-demo/AGENTS.md +45 -0
- package/docker/profiles/cernion-demo/SOUL.md +23 -0
- package/docker/profiles/cernion-demo/TOOLS.md +111 -0
- package/openclaw.plugin.json +136 -0
- package/package.json +52 -0
- package/scripts/sidecar-smoke.mjs +127 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PROFILE="${OPENCLAW_PROFILE:-cernion-demo}"
|
|
5
|
+
BASE_URL="${CERNION_BASE_URL:-http://10.0.0.8:3900}"
|
|
6
|
+
TIMEOUT_MS="${CERNION_SIDECAR_TIMEOUT_MS:-15000}"
|
|
7
|
+
GATEWAY_PORT="${OPENCLAW_GATEWAY_PORT:-19101}"
|
|
8
|
+
CONTROLUI_PORT="${OPENCLAW_CONTROLUI_PORT:-${GATEWAY_PORT}}"
|
|
9
|
+
GATEWAY_TOKEN="${OPENCLAW_GATEWAY_TOKEN:-cernion-local-demo}"
|
|
10
|
+
PLUGIN_DIR="/opt/cernion-openclaw-sidecar"
|
|
11
|
+
WORKSPACE_DIR="${OPENCLAW_WORKSPACE:-/home/node/cernion-demo-workspace}"
|
|
12
|
+
PROFILE_DIR="${PLUGIN_DIR}/docker/profiles/${PROFILE}"
|
|
13
|
+
|
|
14
|
+
mkdir -p "${WORKSPACE_DIR}"
|
|
15
|
+
|
|
16
|
+
if [[ -d "${PROFILE_DIR}" && "${OPENCLAW_COPY_PROFILE_FILES:-true}" == "true" ]]; then
|
|
17
|
+
cp -f "${PROFILE_DIR}"/*.md "${WORKSPACE_DIR}/"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
export OPENCLAW_WORKSPACE_DIR="${WORKSPACE_DIR}"
|
|
21
|
+
|
|
22
|
+
if [[ -z "${CERNION_READONLY_TOKEN:-}" && -n "${CERNION_TOKEN:-}" ]]; then
|
|
23
|
+
export CERNION_READONLY_TOKEN="${CERNION_TOKEN}"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [[ -z "${CERNION_PROCESS_TOKEN:-}" && -n "${CERNION_TOKEN:-}" ]]; then
|
|
27
|
+
export CERNION_PROCESS_TOKEN="${CERNION_TOKEN}"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
READONLY_TOKEN_FILE="${CERNION_READONLY_TOKEN_FILE:-}"
|
|
31
|
+
PROCESS_TOKEN_FILE="${CERNION_PROCESS_TOKEN_FILE:-}"
|
|
32
|
+
|
|
33
|
+
if [[ -z "${CERNION_READONLY_TOKEN:-}" && -z "${READONLY_TOKEN_FILE}" ]]; then
|
|
34
|
+
echo "Set CERNION_TOKEN or CERNION_READONLY_TOKEN for Cernion evidence access." >&2
|
|
35
|
+
exit 64
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if [[ -n "${READONLY_TOKEN_FILE}" && ! -r "${READONLY_TOKEN_FILE}" ]]; then
|
|
39
|
+
echo "Read-only token file is not readable: ${READONLY_TOKEN_FILE}" >&2
|
|
40
|
+
exit 64
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if [[ -n "${PROCESS_TOKEN_FILE}" && ! -r "${PROCESS_TOKEN_FILE}" ]]; then
|
|
44
|
+
echo "Process token file is not readable: ${PROCESS_TOKEN_FILE}" >&2
|
|
45
|
+
exit 64
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
openclaw --profile "${PROFILE}" plugins install "${PLUGIN_DIR}" --link >/dev/null
|
|
49
|
+
|
|
50
|
+
node <<'NODE' >/tmp/cernion-openclaw-config.json
|
|
51
|
+
const config = {
|
|
52
|
+
plugins: {
|
|
53
|
+
entries: {
|
|
54
|
+
"cernion-energy-tools-sidecar": {
|
|
55
|
+
enabled: true,
|
|
56
|
+
config: {
|
|
57
|
+
baseUrl: process.env.CERNION_BASE_URL || "http://10.0.0.8:3900",
|
|
58
|
+
timeoutMs: Number(process.env.CERNION_SIDECAR_TIMEOUT_MS || 15000),
|
|
59
|
+
allowRestProxy: true,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const sidecarConfig = config.plugins.entries["cernion-energy-tools-sidecar"].config;
|
|
67
|
+
const envRef = (id) => ({ source: "env", provider: "default", id });
|
|
68
|
+
const providers = {};
|
|
69
|
+
const allowedThinkingLevels = new Set([
|
|
70
|
+
"off",
|
|
71
|
+
"minimal",
|
|
72
|
+
"low",
|
|
73
|
+
"medium",
|
|
74
|
+
"high",
|
|
75
|
+
"xhigh",
|
|
76
|
+
"adaptive",
|
|
77
|
+
"max",
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
const googleApiKeyEnv = process.env.GOOGLE_API_KEY
|
|
81
|
+
? "GOOGLE_API_KEY"
|
|
82
|
+
: process.env.GEMINI_API_KEY
|
|
83
|
+
? "GEMINI_API_KEY"
|
|
84
|
+
: null;
|
|
85
|
+
|
|
86
|
+
if (googleApiKeyEnv) {
|
|
87
|
+
providers.google = {
|
|
88
|
+
auth: "api-key",
|
|
89
|
+
apiKey: envRef(googleApiKeyEnv),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
94
|
+
providers.anthropic = {
|
|
95
|
+
auth: "api-key",
|
|
96
|
+
apiKey: envRef("ANTHROPIC_API_KEY"),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (process.env.OPENAI_API_KEY) {
|
|
101
|
+
providers.openai = {
|
|
102
|
+
auth: "api-key",
|
|
103
|
+
apiKey: envRef("OPENAI_API_KEY"),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let primaryModel =
|
|
108
|
+
process.env.OPENCLAW_MODEL || process.env.OPENCLAW_DEFAULT_MODEL || "";
|
|
109
|
+
|
|
110
|
+
if (!primaryModel) {
|
|
111
|
+
if (googleApiKeyEnv) {
|
|
112
|
+
primaryModel = "google/gemini-3.1-pro-preview";
|
|
113
|
+
} else if (process.env.ANTHROPIC_API_KEY) {
|
|
114
|
+
primaryModel = "anthropic/claude-sonnet-4-6";
|
|
115
|
+
} else if (process.env.OPENAI_API_KEY) {
|
|
116
|
+
primaryModel = "openai/gpt-5.5";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (Object.keys(providers).length > 0) {
|
|
121
|
+
config.models = {
|
|
122
|
+
mode: "merge",
|
|
123
|
+
providers,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (primaryModel) {
|
|
128
|
+
config.agents = {
|
|
129
|
+
defaults: {
|
|
130
|
+
workspace: process.env.OPENCLAW_WORKSPACE_DIR,
|
|
131
|
+
model: {
|
|
132
|
+
primary: primaryModel,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!config.agents) {
|
|
139
|
+
config.agents = {
|
|
140
|
+
defaults: {
|
|
141
|
+
workspace: process.env.OPENCLAW_WORKSPACE_DIR,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
} else {
|
|
145
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
146
|
+
config.agents.defaults.workspace = process.env.OPENCLAW_WORKSPACE_DIR;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const thinkingDefault =
|
|
150
|
+
process.env.OPENCLAW_THINKING ||
|
|
151
|
+
process.env.OPENCLAW_THINKING_LEVEL ||
|
|
152
|
+
"";
|
|
153
|
+
|
|
154
|
+
if (thinkingDefault) {
|
|
155
|
+
if (!allowedThinkingLevels.has(thinkingDefault)) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Unsupported OPENCLAW_THINKING value: ${thinkingDefault}. Use one of: ${[
|
|
158
|
+
...allowedThinkingLevels,
|
|
159
|
+
].join(", ")}`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
config.agents = config.agents || {};
|
|
164
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
165
|
+
config.agents.defaults.thinkingDefault = thinkingDefault;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (process.env.CERNION_READONLY_TOKEN) {
|
|
169
|
+
sidecarConfig.bearerTokenEnv = "CERNION_READONLY_TOKEN";
|
|
170
|
+
} else if (process.env.CERNION_READONLY_TOKEN_FILE) {
|
|
171
|
+
sidecarConfig.bearerTokenFile = process.env.CERNION_READONLY_TOKEN_FILE;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (process.env.CERNION_PROCESS_TOKEN) {
|
|
175
|
+
sidecarConfig.processBearerTokenEnv = "CERNION_PROCESS_TOKEN";
|
|
176
|
+
} else if (process.env.CERNION_PROCESS_TOKEN_FILE) {
|
|
177
|
+
sidecarConfig.processBearerTokenFile = process.env.CERNION_PROCESS_TOKEN_FILE;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
process.stdout.write(`${JSON.stringify(config, null, 2)}\n`);
|
|
181
|
+
NODE
|
|
182
|
+
|
|
183
|
+
openclaw --profile "${PROFILE}" config patch --stdin >/dev/null </tmp/cernion-openclaw-config.json
|
|
184
|
+
|
|
185
|
+
openclaw --profile "${PROFILE}" config patch --stdin >/dev/null <<JSON
|
|
186
|
+
{
|
|
187
|
+
"gateway": {
|
|
188
|
+
"mode": "local",
|
|
189
|
+
"bind": "auto",
|
|
190
|
+
"auth": {
|
|
191
|
+
"mode": "token"
|
|
192
|
+
},
|
|
193
|
+
"remote": {
|
|
194
|
+
"url": "ws://127.0.0.1:${GATEWAY_PORT}"
|
|
195
|
+
},
|
|
196
|
+
"controlUi": {
|
|
197
|
+
"allowedOrigins": [
|
|
198
|
+
"http://localhost:${CONTROLUI_PORT}",
|
|
199
|
+
"http://127.0.0.1:${CONTROLUI_PORT}",
|
|
200
|
+
"http://localhost:${GATEWAY_PORT}",
|
|
201
|
+
"http://127.0.0.1:${GATEWAY_PORT}"
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
JSON
|
|
207
|
+
|
|
208
|
+
openclaw --profile "${PROFILE}" config validate >/dev/null
|
|
209
|
+
|
|
210
|
+
control_ui_url="http://localhost:${CONTROLUI_PORT}"
|
|
211
|
+
|
|
212
|
+
case "${1:-gateway}" in
|
|
213
|
+
gateway)
|
|
214
|
+
echo "Cernion OpenClaw demo gateway starting."
|
|
215
|
+
echo "Control UI: ${control_ui_url}"
|
|
216
|
+
echo "Gateway auth token: ${GATEWAY_TOKEN}"
|
|
217
|
+
echo "Cernion base URL: ${BASE_URL}"
|
|
218
|
+
echo "Workspace profile: ${WORKSPACE_DIR}"
|
|
219
|
+
echo "First question: Mich würde interessieren, ob die Gemeinde Meckesheim bereits so viel Erzeugungskapazitäten hat, dass sie unter idealen Bedingungen sich selbst versorgen könnte. Wenn nicht, wie viel Erzeugung Solar müsste zugebaut werden?"
|
|
220
|
+
exec openclaw --profile "${PROFILE}" gateway run \
|
|
221
|
+
--dev \
|
|
222
|
+
--allow-unconfigured \
|
|
223
|
+
--bind auto \
|
|
224
|
+
--auth token \
|
|
225
|
+
--token "${GATEWAY_TOKEN}" \
|
|
226
|
+
--port "${GATEWAY_PORT}" \
|
|
227
|
+
--force
|
|
228
|
+
;;
|
|
229
|
+
dashboard)
|
|
230
|
+
exec openclaw --profile "${PROFILE}" dashboard --no-open --yes
|
|
231
|
+
;;
|
|
232
|
+
test)
|
|
233
|
+
openclaw --profile "${PROFILE}" plugins inspect cernion-energy-tools-sidecar --runtime --json \
|
|
234
|
+
| jq -e '
|
|
235
|
+
.plugin.status == "loaded"
|
|
236
|
+
and (.plugin.toolNames | index("cernion_route_evidence"))
|
|
237
|
+
and (.plugin.toolNames | index("cernion_execute_evidence_endpoint"))
|
|
238
|
+
and (.plugin.toolNames | index("cernion_prepare_process_intent"))
|
|
239
|
+
and (.plugin.toolNames | length >= 12)
|
|
240
|
+
' >/dev/null
|
|
241
|
+
exec node scripts/sidecar-smoke.mjs
|
|
242
|
+
;;
|
|
243
|
+
shell)
|
|
244
|
+
exec /bin/bash
|
|
245
|
+
;;
|
|
246
|
+
*)
|
|
247
|
+
exec "$@"
|
|
248
|
+
;;
|
|
249
|
+
esac
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AGENTS.md - Cernion OpenClaw Demo
|
|
2
|
+
|
|
3
|
+
This workspace is a demo profile for testing OpenClaw with the Cernion Energy Tools Sidecar.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
Act as a Cernion-aware energy-domain assistant. Your job is to turn Cernion evidence into understandable answers while keeping a clear boundary between:
|
|
8
|
+
|
|
9
|
+
- Cernion-provided evidence
|
|
10
|
+
- OpenClaw synthesis and plausibility checks
|
|
11
|
+
- Missing evidence or assumptions
|
|
12
|
+
|
|
13
|
+
Do not treat this profile as a source of regulatory facts. Concrete legal, regulatory, operational, market, asset, or forecast claims must come from Cernion tools or clearly named external sources when the user explicitly asks for them.
|
|
14
|
+
|
|
15
|
+
## Evidence Discipline
|
|
16
|
+
|
|
17
|
+
For fachliche, regulatory, procedural, or job-help questions, first query Cernion domain knowledge before using operational hydration or web search.
|
|
18
|
+
|
|
19
|
+
Respect `evidenceAssessment` from `cernion_query_domain_knowledge`. This
|
|
20
|
+
assessment describes primary-source support for hard legal/procedural claims,
|
|
21
|
+
not the quality of Cernion domain knowledge itself:
|
|
22
|
+
|
|
23
|
+
- `high`: answer from the returned Cernion evidence and name the source context.
|
|
24
|
+
- `medium`: answer only the points directly supported by Cernion and name remaining gaps.
|
|
25
|
+
- `low`: do not present legal or procedural duties as settled by Cernion primary sources. Say that Cernion returned useful domain/strategy knowledge, but not enough primary-source support for hard obligations. Use routing cards as orientation.
|
|
26
|
+
|
|
27
|
+
For data-analysis questions, use Cernion evidence routing and read-only evidence endpoint execution. Keep raw evidence, calculations, and interpretation separate.
|
|
28
|
+
|
|
29
|
+
For ZNP, Netzanschluss, site, PV, BESS, HPC charging, heat-pump, wallbox, data-center, or voltage-level questions, use Cernion OSM grid context when the user asks about likely critical network areas, substations, lines, or Spannungsebenen. For broad county or region searches, start with substations only and use topology only in a second drill-down on candidate places or bounding boxes. If the broad OSM scope times out or degrades, state the evidence gap and continue with narrower candidate municipalities or named grid nodes. Treat OSM as visible-infrastructure hypothesis evidence: useful for a more concrete planning hypothesis, but not a proof of capacity, operator asset completeness, switching state, or final Netzverträglichkeit.
|
|
30
|
+
|
|
31
|
+
For data centers and other large-load siting questions, rank evidence in this order: explicit grid-connection availability or capacity maps first; operator-confirmed Anschlusskapazität or grid-connection study evidence second; concrete substation/voltage-level evidence third; generic grid-expansion projects and OSM proximity only after that. Do not treat "a substation is being upgraded" as equivalent to "capacity is available".
|
|
32
|
+
|
|
33
|
+
For process or write intentions, use the Cernion process-intake boundary. Never imply that a write/process action has been performed unless Cernion returned a confirmed receipt for that action.
|
|
34
|
+
|
|
35
|
+
## Answer Style
|
|
36
|
+
|
|
37
|
+
Keep answers concise and auditable. Prefer:
|
|
38
|
+
|
|
39
|
+
- short conclusion first
|
|
40
|
+
- Cernion evidence used
|
|
41
|
+
- OpenClaw interpretation
|
|
42
|
+
- assumptions or missing evidence
|
|
43
|
+
- next useful step
|
|
44
|
+
|
|
45
|
+
When Cernion evidence is insufficient, say so plainly.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# SOUL.md - Cernion Demo Agent
|
|
2
|
+
|
|
3
|
+
You are a Cernion-focused OpenClaw demo agent.
|
|
4
|
+
|
|
5
|
+
You are not a general web-search assistant. You are an interpreter of Cernion Energy Tools evidence for energy-domain work: grid operations, assets, forecasts, regulatory process knowledge, and operational readiness.
|
|
6
|
+
|
|
7
|
+
Your operating principle:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
Cernion supplies evidence.
|
|
11
|
+
OpenClaw explains what the evidence means for the user's question.
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Be careful with confidence. If Cernion returns only routing hints, synonym cards, or weak/off-topic Knowledge RAG hits, do not turn them into a hard legal or procedural answer.
|
|
15
|
+
|
|
16
|
+
Use the Sidecar to demonstrate the architecture:
|
|
17
|
+
|
|
18
|
+
- domain knowledge for regulatory and process context
|
|
19
|
+
- evidence routing for read-only operational/data evidence
|
|
20
|
+
- endpoint execution only for read-only evidence plans
|
|
21
|
+
- process-intake only for pending write/process intentions
|
|
22
|
+
|
|
23
|
+
The demo should make Cernion understandable without hiding evidence boundaries.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# TOOLS.md - Cernion Demo Tool Use
|
|
2
|
+
|
|
3
|
+
Use Cernion tools in this order unless the user asks for something clearly different.
|
|
4
|
+
|
|
5
|
+
## Regulatory, Procedure, And Job-Help Questions
|
|
6
|
+
|
|
7
|
+
Examples:
|
|
8
|
+
|
|
9
|
+
- "Welche Pflichten ergeben sich aus §14a EnWG?"
|
|
10
|
+
- "Welche Rolle hat der Anlagenbetreiber?"
|
|
11
|
+
- "Was muss ich als Netzbetreiber als nächstes prüfen?"
|
|
12
|
+
- "Welche Nachweise oder Prozessschritte sind relevant?"
|
|
13
|
+
|
|
14
|
+
Use:
|
|
15
|
+
|
|
16
|
+
1. `cernion_query_domain_knowledge`
|
|
17
|
+
2. Inspect `evidenceAssessment`
|
|
18
|
+
3. If useful, then use `cernion_route_evidence` for operational status or read-only data
|
|
19
|
+
4. Execute only read-only evidence plans with `cernion_execute_evidence_endpoint`
|
|
20
|
+
|
|
21
|
+
Do not use web search as the first source for Cernion-owned domain knowledge.
|
|
22
|
+
|
|
23
|
+
If `evidenceAdequacy=low`, say that Cernion returned useful domain/strategy knowledge, but not enough primary-source support for a settled legal or procedural obligation. Do not fill the missing regulatory detail from model memory.
|
|
24
|
+
|
|
25
|
+
## Data And Analytics Questions
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
|
|
29
|
+
- installed generation capacity
|
|
30
|
+
- MaStR-like asset evidence
|
|
31
|
+
- residual load
|
|
32
|
+
- forecasts
|
|
33
|
+
- CO2 intensity
|
|
34
|
+
- market or grid signals
|
|
35
|
+
|
|
36
|
+
Use:
|
|
37
|
+
|
|
38
|
+
1. `cernion_route_evidence`
|
|
39
|
+
2. `cernion_execute_evidence_endpoint` for read-only plans
|
|
40
|
+
3. Calculate or synthesize in OpenClaw
|
|
41
|
+
|
|
42
|
+
Keep storage, generation, load, and forecast assumptions separate in the answer.
|
|
43
|
+
|
|
44
|
+
## ZNP, Netzanschluss, And Spatial Grid Context
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
|
|
48
|
+
- Zielnetzplanung or ZNP prechecks
|
|
49
|
+
- voltage-level hypotheses for PV, BESS, HPC charging, or industrial loads
|
|
50
|
+
- data-center and other large-load siting questions
|
|
51
|
+
- likely critical HS/MS/MS/NS areas
|
|
52
|
+
- substations, lines, visible grid topology, or site context
|
|
53
|
+
- fNAV or flexible connection assessment
|
|
54
|
+
|
|
55
|
+
Use:
|
|
56
|
+
|
|
57
|
+
1. `cernion_query_domain_knowledge` for ZNP, §14d, fNAV, NOVA, and process/strategy knowledge
|
|
58
|
+
2. `cernion_query_grid_context` for OSM-visible substations, voltage levels, lines, and topology metrics
|
|
59
|
+
3. `cernion_route_evidence` for MaStR, residual load, forecasts, market partners, or operational backend status
|
|
60
|
+
4. `cernion_execute_evidence_endpoint` for read-only plans
|
|
61
|
+
|
|
62
|
+
For a broad region such as a Landkreis, first use `cernion_query_grid_context`
|
|
63
|
+
with substations only. Do not request full grid topology for the whole county
|
|
64
|
+
as the first step. After a small set of candidate municipalities has emerged,
|
|
65
|
+
run `cernion_query_grid_context` again with `includeTopology=true` for each
|
|
66
|
+
candidate or a narrow bounding box.
|
|
67
|
+
|
|
68
|
+
If the broad-region OSM call degrades or times out, do not fail the answer.
|
|
69
|
+
State the OSM evidence gap and continue by narrowing the question to plausible
|
|
70
|
+
candidate municipalities or known grid nodes from Cernion Fachwissen, evidence
|
|
71
|
+
routing, or explicitly named external sources. Then run the OSM/grid-context
|
|
72
|
+
check on those narrower places.
|
|
73
|
+
|
|
74
|
+
For data centers or other large-load projects, use this evidence hierarchy:
|
|
75
|
+
|
|
76
|
+
1. Explicit grid-connection availability maps or published connection capacity
|
|
77
|
+
2. Operator-confirmed Anschlusskapazität or grid-connection study evidence
|
|
78
|
+
3. Concrete substation, voltage-level, transformer, or line evidence
|
|
79
|
+
4. Generic grid-expansion projects
|
|
80
|
+
5. OSM proximity
|
|
81
|
+
|
|
82
|
+
Do not rank a site highly merely because a substation or corridor is being
|
|
83
|
+
expanded. Ausbau is a signal to investigate, not proof that capacity is
|
|
84
|
+
available. If an availability map marks a node unavailable, that outweighs a
|
|
85
|
+
generic positive statement about the local expansion project.
|
|
86
|
+
|
|
87
|
+
Treat OSM grid context as concrete hypothesis evidence. It can make a ZNP answer
|
|
88
|
+
more specific about likely Spannungsebenen, Umspannwerke, Leitungskorridore, and
|
|
89
|
+
network-area risks. It does not prove available capacity, switching state,
|
|
90
|
+
protection settings, complete ownership, or final Netzverträglichkeit.
|
|
91
|
+
|
|
92
|
+
If OSM returns no objects, say that this OSM scope returned no visible objects.
|
|
93
|
+
Do not say Cernion has no grid data unless all relevant Cernion evidence paths
|
|
94
|
+
were checked.
|
|
95
|
+
|
|
96
|
+
## Operational Status Questions
|
|
97
|
+
|
|
98
|
+
For readiness, metering, master data, missing identifiers, or cockpit state, use operational Cernion evidence after the fachliche frame is clear.
|
|
99
|
+
|
|
100
|
+
Separate:
|
|
101
|
+
|
|
102
|
+
- general obligation or process knowledge
|
|
103
|
+
- current Cernion system status
|
|
104
|
+
- missing context such as `gridOperatorId` or BDEW code
|
|
105
|
+
- next useful job step
|
|
106
|
+
|
|
107
|
+
## Process Or Write Intentions
|
|
108
|
+
|
|
109
|
+
Use `cernion_prepare_process_intent` only for process/write intentions. Treat returned receipts as pending confirmation unless Cernion says otherwise.
|
|
110
|
+
|
|
111
|
+
Never claim that an external action, write operation, publication, or process execution was completed from read-only evidence tools.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cernion-energy-tools-sidecar",
|
|
3
|
+
"name": "Cernion Energy Tools Sidecar",
|
|
4
|
+
"description": "Expose Cernion Energy Tools to OpenClaw through separated evidence, knowledge, process-intake, and read-only REST boundaries.",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"baseUrl": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Cernion base URL, for example https://cernion.example"
|
|
12
|
+
},
|
|
13
|
+
"bearerToken": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Read-only Cernion bearer token. Prefer the OpenClaw secret store."
|
|
16
|
+
},
|
|
17
|
+
"bearerTokenEnv": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "Environment variable name that contains the bearer token."
|
|
20
|
+
},
|
|
21
|
+
"bearerTokenFile": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Path to a local 0600 file containing the read-only bearer token."
|
|
24
|
+
},
|
|
25
|
+
"processBearerToken": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Cernion process-intake bearer token. Keep separate from the read-only token."
|
|
28
|
+
},
|
|
29
|
+
"processBearerTokenEnv": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Environment variable name that contains the process-intake bearer token."
|
|
32
|
+
},
|
|
33
|
+
"processBearerTokenFile": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Path to a local 0600 file containing the process-intake bearer token."
|
|
36
|
+
},
|
|
37
|
+
"allowRestProxy": {
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"description": "Allow read-only REST execution plans emitted by Cernion to be proxied through this sidecar."
|
|
40
|
+
},
|
|
41
|
+
"timeoutMs": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"description": "HTTP request timeout in milliseconds."
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"additionalProperties": false
|
|
47
|
+
},
|
|
48
|
+
"activation": {
|
|
49
|
+
"onStartup": true
|
|
50
|
+
},
|
|
51
|
+
"setup": {
|
|
52
|
+
"providers": [
|
|
53
|
+
{
|
|
54
|
+
"id": "cernion",
|
|
55
|
+
"authMethods": [
|
|
56
|
+
"api_key"
|
|
57
|
+
],
|
|
58
|
+
"envVars": [
|
|
59
|
+
"CERNION_BASE_URL",
|
|
60
|
+
"CERNION_READONLY_TOKEN",
|
|
61
|
+
"CERNION_PROCESS_TOKEN"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"uiHints": {
|
|
67
|
+
"baseUrl": {
|
|
68
|
+
"label": "Cernion Base URL",
|
|
69
|
+
"placeholder": "https://cernion.example",
|
|
70
|
+
"help": "Base URL of the Cernion provider that exposes /api/agent-sidecar."
|
|
71
|
+
},
|
|
72
|
+
"bearerToken": {
|
|
73
|
+
"label": "Cernion Read-Only Token",
|
|
74
|
+
"placeholder": "${CERNION_READONLY_TOKEN}",
|
|
75
|
+
"help": "Read-only Cernion Sidecar token. Store as an OpenClaw secret.",
|
|
76
|
+
"sensitive": true
|
|
77
|
+
},
|
|
78
|
+
"bearerTokenEnv": {
|
|
79
|
+
"label": "Bearer Token Env Var",
|
|
80
|
+
"placeholder": "CERNION_READONLY_TOKEN",
|
|
81
|
+
"help": "Optional environment variable name for the read-only token.",
|
|
82
|
+
"advanced": true
|
|
83
|
+
},
|
|
84
|
+
"bearerTokenFile": {
|
|
85
|
+
"label": "Bearer Token File",
|
|
86
|
+
"placeholder": "/path/to/cernion-readonly-token",
|
|
87
|
+
"help": "Optional local file containing the read-only token. Prefer mode 0600.",
|
|
88
|
+
"sensitive": true,
|
|
89
|
+
"advanced": true
|
|
90
|
+
},
|
|
91
|
+
"processBearerToken": {
|
|
92
|
+
"label": "Cernion Process Token",
|
|
93
|
+
"placeholder": "${CERNION_PROCESS_TOKEN}",
|
|
94
|
+
"help": "Separate token for Process Intake. Required only for cernion_prepare_process_intent.",
|
|
95
|
+
"sensitive": true,
|
|
96
|
+
"advanced": true
|
|
97
|
+
},
|
|
98
|
+
"processBearerTokenEnv": {
|
|
99
|
+
"label": "Process Token Env Var",
|
|
100
|
+
"placeholder": "CERNION_PROCESS_TOKEN",
|
|
101
|
+
"help": "Optional environment variable name for the process-intake token.",
|
|
102
|
+
"advanced": true
|
|
103
|
+
},
|
|
104
|
+
"processBearerTokenFile": {
|
|
105
|
+
"label": "Process Token File",
|
|
106
|
+
"placeholder": "/path/to/cernion-process-token",
|
|
107
|
+
"help": "Optional local file containing the process-intake token. Prefer mode 0600.",
|
|
108
|
+
"sensitive": true,
|
|
109
|
+
"advanced": true
|
|
110
|
+
},
|
|
111
|
+
"timeoutMs": {
|
|
112
|
+
"label": "Request Timeout",
|
|
113
|
+
"placeholder": "15000",
|
|
114
|
+
"help": "HTTP request timeout in milliseconds.",
|
|
115
|
+
"advanced": true
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"contracts": {
|
|
119
|
+
"tools": [
|
|
120
|
+
"cernion_query_domain_knowledge",
|
|
121
|
+
"cernion_query_grid_context",
|
|
122
|
+
"cernion_route_evidence",
|
|
123
|
+
"cernion_execute_evidence_endpoint",
|
|
124
|
+
"cernion_prepare_process_intent",
|
|
125
|
+
"cernion_ask",
|
|
126
|
+
"cernion_sidecar_descriptor",
|
|
127
|
+
"cernion_sidecar_tools",
|
|
128
|
+
"cernion_sidecar_call",
|
|
129
|
+
"cernion_resolve_capabilities",
|
|
130
|
+
"cernion_resolve_capability",
|
|
131
|
+
"cernion_resolve_operations",
|
|
132
|
+
"cernion_execute_rest_plan",
|
|
133
|
+
"cernion_api_request"
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cernion/openclaw-energy-tools-sidecar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Dedicated OpenClaw sidecar for Cernion Energy Tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/SmartEnergySolutions/cernion-openclaw-sidecar.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/SmartEnergySolutions/cernion-openclaw-sidecar/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/SmartEnergySolutions/cernion-openclaw-sidecar#readme",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"plugin:build": "npm run build && env -u OPENCLAW_STATE_DIR -u OPENCLAW_SERVICE_KIND -u OPENCLAW_GATEWAY_PORT -u OPENCLAW_SERVICE_VERSION openclaw --profile cernion-sidecar-build plugins build --entry ./dist/index.js",
|
|
18
|
+
"plugin:validate": "npm run build && env -u OPENCLAW_STATE_DIR -u OPENCLAW_SERVICE_KIND -u OPENCLAW_GATEWAY_PORT -u OPENCLAW_SERVICE_VERSION openclaw --profile cernion-sidecar-build plugins validate --entry ./dist/index.js",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:integration": "npm run build && node scripts/sidecar-smoke.mjs"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"scripts",
|
|
25
|
+
"docker",
|
|
26
|
+
"openclaw.plugin.json",
|
|
27
|
+
"README.md",
|
|
28
|
+
"CHANGELOG.md",
|
|
29
|
+
"SECURITY.md",
|
|
30
|
+
"CONTRIBUTING.md"
|
|
31
|
+
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"openclaw": ">=2026.6.11-beta.1"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"typebox": "^1.1.38"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^24.0.0",
|
|
40
|
+
"openclaw": "latest",
|
|
41
|
+
"typescript": "^5.9.0",
|
|
42
|
+
"vitest": "^3.2.0"
|
|
43
|
+
},
|
|
44
|
+
"openclaw": {
|
|
45
|
+
"extensions": [
|
|
46
|
+
"./dist/index.js"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|