ocuclaw 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/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # OcuClaw
2
+
3
+ OcuClaw is an OpenClaw plugin for Even G2 smart glasses.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ openclaw plugins install ocuclaw
9
+ openclaw plugins enable ocuclaw
10
+ ```
11
+
12
+ ## Configure
13
+
14
+ Set the relay token before starting or restarting the OpenClaw gateway:
15
+
16
+ ```bash
17
+ openclaw config set plugins.entries.ocuclaw.config.relayToken "<relay-token>"
18
+ ```
19
+
20
+ Optional settings live under `plugins.entries.ocuclaw.config.*`.
21
+
22
+ ## Notes
23
+
24
+ - Plugin id: `ocuclaw`
25
+ - The WebUI is a separate artifact and is not bundled into this npm package.
@@ -0,0 +1,165 @@
1
+ import { normalizeEvenAiRoutingMode } from "../even-ai/even-ai-settings-store.js";
2
+
3
+ function isObject(value) {
4
+ return value && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+
7
+ function parseBool(value, defaultValue) {
8
+ if (value === undefined || value === null || value === "") return defaultValue;
9
+ if (typeof value === "boolean") return value;
10
+ if (typeof value === "number") return value !== 0;
11
+ return String(value).toLowerCase() !== "false";
12
+ }
13
+
14
+ function parseIntOrDefault(value, defaultValue) {
15
+ if (value === undefined || value === null || value === "") return defaultValue;
16
+ const parsed = parseInt(String(value), 10);
17
+ if (Number.isNaN(parsed)) return defaultValue;
18
+ return parsed;
19
+ }
20
+
21
+ function parseEvenAiRoutingMode(value) {
22
+ return normalizeEvenAiRoutingMode(value);
23
+ }
24
+
25
+ function parseEvenAiDedicatedSessionKey(value) {
26
+ const trimmed = pickString(value);
27
+ if (!trimmed) {
28
+ return "ocuclaw:even-ai";
29
+ }
30
+ return trimmed.toLowerCase().startsWith("ocuclaw:")
31
+ ? trimmed
32
+ : "ocuclaw:even-ai";
33
+ }
34
+
35
+ function parseJsonOrUndefined(value, envName) {
36
+ if (value === undefined || value === null || value === "") return undefined;
37
+ try {
38
+ return JSON.parse(String(value));
39
+ } catch (err) {
40
+ throw new Error(`${envName} must be valid JSON: ${err.message}`);
41
+ }
42
+ }
43
+
44
+ function pickString(...values) {
45
+ for (const value of values) {
46
+ if (typeof value !== "string") continue;
47
+ const trimmed = value.trim();
48
+ if (trimmed) return trimmed;
49
+ }
50
+ return "";
51
+ }
52
+
53
+ function pickValue(...values) {
54
+ for (const value of values) {
55
+ if (value !== undefined && value !== null && value !== "") {
56
+ return value;
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+
62
+ function resolveGatewayUrlFromOpenClawConfig(openclawConfig) {
63
+ if (!isObject(openclawConfig) || !isObject(openclawConfig.gateway)) {
64
+ return "";
65
+ }
66
+ const gateway = openclawConfig.gateway;
67
+ const remoteMode = gateway.mode === "remote";
68
+ if (remoteMode && isObject(gateway.remote)) {
69
+ const remoteUrl = pickString(gateway.remote.url);
70
+ if (remoteUrl) {
71
+ return remoteUrl;
72
+ }
73
+ }
74
+ const scheme = gateway.tls && gateway.tls.enabled === true ? "wss" : "ws";
75
+ const port = parseIntOrDefault(gateway.port, 18789);
76
+ return `${scheme}://127.0.0.1:${port}`;
77
+ }
78
+
79
+ function resolveGatewayTokenFromOpenClawConfig(openclawConfig) {
80
+ if (!isObject(openclawConfig) || !isObject(openclawConfig.gateway)) {
81
+ return "";
82
+ }
83
+ const gateway = openclawConfig.gateway;
84
+ const remoteMode = gateway.mode === "remote";
85
+ if (remoteMode && isObject(gateway.remote)) {
86
+ return pickString(gateway.remote.token);
87
+ }
88
+ return pickString(
89
+ isObject(gateway.auth) ? gateway.auth.token : undefined,
90
+ );
91
+ }
92
+
93
+ function resolveDebugNoisyPolicies(pluginValue, envValue) {
94
+ if (pluginValue !== undefined && pluginValue !== null) {
95
+ return pluginValue;
96
+ }
97
+ return parseJsonOrUndefined(envValue, "debugNoisyPolicies");
98
+ }
99
+
100
+ export function createRuntimeConfig(opts = {}) {
101
+ const pluginConfig = isObject(opts.pluginConfig) ? opts.pluginConfig : {};
102
+ const openclawConfig = isObject(opts.openclawConfig) ? opts.openclawConfig : {};
103
+ const relayToken = pickString(pluginConfig.relayToken);
104
+ const gatewayUrl = pickString(resolveGatewayUrlFromOpenClawConfig(openclawConfig));
105
+ const gatewayToken = pickString(resolveGatewayTokenFromOpenClawConfig(openclawConfig));
106
+
107
+ if (!relayToken) {
108
+ throw new Error(
109
+ "OcuClaw relayToken is required. Set plugins.entries.ocuclaw.config.relayToken.",
110
+ );
111
+ }
112
+ if (!gatewayUrl) {
113
+ throw new Error(
114
+ "OcuClaw gatewayUrl is required from api.config.gateway. OpenClaw gateway config is missing or unusable.",
115
+ );
116
+ }
117
+ if (!gatewayToken) {
118
+ throw new Error(
119
+ "OcuClaw gatewayToken is required from api.config.gateway. OpenClaw gateway auth token is missing or unusable.",
120
+ );
121
+ }
122
+
123
+ return {
124
+ gatewayUrl,
125
+ gatewayToken,
126
+ relayToken,
127
+ wsBind: pickString(pluginConfig.wsBind, "127.0.0.1"),
128
+ wsPort: parseIntOrDefault(pickValue(pluginConfig.wsPort), 9000),
129
+ sessionLimit: parseIntOrDefault(pickValue(pluginConfig.sessionLimit), 10),
130
+ sonioxApiKey: pickString(pluginConfig.sonioxApiKey),
131
+ debugPayloadMaxBytes: parseIntOrDefault(
132
+ pickValue(pluginConfig.debugPayloadMaxBytes),
133
+ 2048,
134
+ ),
135
+ debugNoisyPolicies: resolveDebugNoisyPolicies(
136
+ pluginConfig.debugNoisyPolicies,
137
+ undefined,
138
+ ),
139
+ externalDebugToolsEnabled: parseBool(
140
+ pluginConfig.externalDebugToolsEnabled,
141
+ false,
142
+ ),
143
+ evenAiEnabled: parseBool(pluginConfig.evenAiEnabled, false),
144
+ evenAiToken: pickString(pluginConfig.evenAiToken),
145
+ evenAiSystemPrompt: pickString(pluginConfig.evenAiSystemPrompt),
146
+ evenAiRequestTimeoutMs: parseIntOrDefault(
147
+ pluginConfig.evenAiRequestTimeoutMs,
148
+ 60000,
149
+ ),
150
+ evenAiMaxBodyBytes: parseIntOrDefault(
151
+ pluginConfig.evenAiMaxBodyBytes,
152
+ 65536,
153
+ ),
154
+ evenAiDedupWindowMs: parseIntOrDefault(
155
+ pluginConfig.evenAiDedupWindowMs,
156
+ 500,
157
+ ),
158
+ evenAiRoutingMode: parseEvenAiRoutingMode(pluginConfig.evenAiRoutingMode),
159
+ evenAiDedicatedSessionKey: parseEvenAiDedicatedSessionKey(
160
+ pluginConfig.evenAiDedicatedSessionKey,
161
+ ),
162
+ };
163
+ }
164
+
165
+ export default createRuntimeConfig;