@nordbyte/nordrelay 0.3.1 → 0.4.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/.env.example +45 -2
- package/README.md +204 -30
- package/dist/agent-activity.js +300 -0
- package/dist/agent-adapter.js +17 -30
- package/dist/agent-factory.js +27 -0
- package/dist/agent.js +123 -9
- package/dist/artifacts.js +1 -1
- package/dist/audit-log.js +1 -1
- package/dist/bot-ui.js +1 -1
- package/dist/bot.js +328 -159
- package/dist/claude-code-auth.js +121 -0
- package/dist/claude-code-cli.js +19 -0
- package/dist/claude-code-launch.js +73 -0
- package/dist/claude-code-session.js +660 -0
- package/dist/claude-code-state.js +590 -0
- package/dist/codex-session.js +12 -1
- package/dist/config.js +113 -9
- package/dist/hermes-api.js +150 -0
- package/dist/hermes-auth.js +96 -0
- package/dist/hermes-cli.js +19 -0
- package/dist/hermes-launch.js +57 -0
- package/dist/hermes-session.js +477 -0
- package/dist/hermes-state.js +609 -0
- package/dist/index.js +51 -8
- package/dist/openclaw-auth.js +27 -0
- package/dist/openclaw-cli.js +19 -0
- package/dist/openclaw-gateway.js +285 -0
- package/dist/openclaw-launch.js +65 -0
- package/dist/openclaw-session.js +549 -0
- package/dist/openclaw-state.js +409 -0
- package/dist/operations.js +83 -2
- package/dist/pi-auth.js +59 -0
- package/dist/pi-launch.js +61 -0
- package/dist/pi-rpc.js +18 -0
- package/dist/pi-session.js +103 -15
- package/dist/pi-state.js +253 -0
- package/dist/relay-runtime.js +673 -51
- package/dist/session-format.js +28 -18
- package/dist/session-registry.js +40 -15
- package/dist/settings-service.js +35 -4
- package/dist/web-dashboard-ui.js +18 -0
- package/dist/web-dashboard.js +329 -47
- package/package.json +8 -3
- package/plugins/nordrelay/.codex-plugin/plugin.json +7 -4
- package/plugins/nordrelay/commands/remote.md +2 -2
- package/plugins/nordrelay/scripts/nordrelay.mjs +131 -3
- package/plugins/nordrelay/skills/telegram-remote/SKILL.md +2 -2
- package/CHANGELOG.md +0 -26
package/dist/session-format.js
CHANGED
|
@@ -17,8 +17,8 @@ export function renderSessionInfoPlain(info) {
|
|
|
17
17
|
capabilities.fastMode
|
|
18
18
|
? `Reasoning/Fast: ${info.reasoningEffort ?? "(model default)"} / ${info.fastMode ? "on" : "off"}`
|
|
19
19
|
: `${agentReasoningLabel(agentId)}: ${info.reasoningEffort ?? "(model default)"}`,
|
|
20
|
-
...renderCodexUsagePlain(info),
|
|
21
|
-
...renderAgentUsagePlain(info),
|
|
20
|
+
...renderCodexUsagePlain(info, capabilities),
|
|
21
|
+
...renderAgentUsagePlain(info, capabilities),
|
|
22
22
|
info.sessionTokens ? formatSessionTokensPlain(info.sessionTokens) : undefined,
|
|
23
23
|
]
|
|
24
24
|
.filter((line) => Boolean(line))
|
|
@@ -42,8 +42,8 @@ export function renderSessionInfoHTML(info) {
|
|
|
42
42
|
capabilities.fastMode
|
|
43
43
|
? `<b>Reasoning/Fast:</b> <code>${escapeHTML(info.reasoningEffort ?? "(model default)")} / ${info.fastMode ? "on" : "off"}</code>`
|
|
44
44
|
: `<b>${escapeHTML(agentReasoningLabel(agentId))}:</b> <code>${escapeHTML(info.reasoningEffort ?? "(model default)")}</code>`,
|
|
45
|
-
...renderCodexUsageHTML(info),
|
|
46
|
-
...renderAgentUsageHTML(info),
|
|
45
|
+
...renderCodexUsageHTML(info, capabilities),
|
|
46
|
+
...renderAgentUsageHTML(info, capabilities),
|
|
47
47
|
info.sessionTokens ? `<b>Session tokens:</b> <code>${escapeHTML(formatSessionTokensValue(info.sessionTokens))}</code>` : undefined,
|
|
48
48
|
]
|
|
49
49
|
.filter((line) => Boolean(line))
|
|
@@ -67,16 +67,16 @@ export function formatFileSize(bytes) {
|
|
|
67
67
|
}
|
|
68
68
|
return `${(bytes / (1024 * 1024)).toFixed(1).replace(/\.0$/, "")} MB`;
|
|
69
69
|
}
|
|
70
|
-
function renderCodexUsagePlain(info) {
|
|
70
|
+
function renderCodexUsagePlain(info, capabilities) {
|
|
71
71
|
const usage = info.codexUsage;
|
|
72
72
|
if (!usage) {
|
|
73
73
|
return [];
|
|
74
74
|
}
|
|
75
75
|
const lines = [];
|
|
76
|
-
if (usage.contextUsedPercent !== null && usage.contextWindow !== null && usage.lastTokenUsage) {
|
|
76
|
+
if (capabilities.usageStats && usage.contextUsedPercent !== null && usage.contextWindow !== null && usage.lastTokenUsage) {
|
|
77
77
|
lines.push(`Context used: ${formatPercent(usage.contextUsedPercent)} (${formatCompactTokenCount(usage.lastTokenUsage.totalTokens)} / ${formatCompactTokenCount(usage.contextWindow)})`);
|
|
78
78
|
}
|
|
79
|
-
if (usage.totalTokenUsage) {
|
|
79
|
+
if (capabilities.usageStats && usage.totalTokenUsage) {
|
|
80
80
|
lines.push([
|
|
81
81
|
`Tokens in: ${formatCompactTokenCount(usage.totalTokenUsage.inputTokens)}`,
|
|
82
82
|
`cached: ${formatCompactTokenCount(usage.totalTokenUsage.cachedInputTokens)}`,
|
|
@@ -84,22 +84,24 @@ function renderCodexUsagePlain(info) {
|
|
|
84
84
|
`reasoning out: ${formatCompactTokenCount(usage.totalTokenUsage.reasoningOutputTokens)}`,
|
|
85
85
|
].join(" · "));
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
if (capabilities.subscriptionLimits) {
|
|
88
|
+
const limits = formatLimitsLeft(usage);
|
|
89
|
+
if (limits) {
|
|
90
|
+
lines.push(`Limits left: ${limits}`);
|
|
91
|
+
}
|
|
90
92
|
}
|
|
91
93
|
return lines;
|
|
92
94
|
}
|
|
93
|
-
function renderCodexUsageHTML(info) {
|
|
95
|
+
function renderCodexUsageHTML(info, capabilities) {
|
|
94
96
|
const usage = info.codexUsage;
|
|
95
97
|
if (!usage) {
|
|
96
98
|
return [];
|
|
97
99
|
}
|
|
98
100
|
const lines = [];
|
|
99
|
-
if (usage.contextUsedPercent !== null && usage.contextWindow !== null && usage.lastTokenUsage) {
|
|
101
|
+
if (capabilities.usageStats && usage.contextUsedPercent !== null && usage.contextWindow !== null && usage.lastTokenUsage) {
|
|
100
102
|
lines.push(`<b>Context used:</b> <code>${escapeHTML(formatPercent(usage.contextUsedPercent))}</code> <i>(${escapeHTML(formatCompactTokenCount(usage.lastTokenUsage.totalTokens))} / ${escapeHTML(formatCompactTokenCount(usage.contextWindow))})</i>`);
|
|
101
103
|
}
|
|
102
|
-
if (usage.totalTokenUsage) {
|
|
104
|
+
if (capabilities.usageStats && usage.totalTokenUsage) {
|
|
103
105
|
lines.push(`<b>Tokens:</b> <code>${escapeHTML([
|
|
104
106
|
`in ${formatCompactTokenCount(usage.totalTokenUsage.inputTokens)}`,
|
|
105
107
|
`cached ${formatCompactTokenCount(usage.totalTokenUsage.cachedInputTokens)}`,
|
|
@@ -107,13 +109,18 @@ function renderCodexUsageHTML(info) {
|
|
|
107
109
|
`reasoning out ${formatCompactTokenCount(usage.totalTokenUsage.reasoningOutputTokens)}`,
|
|
108
110
|
].join(" · "))}</code>`);
|
|
109
111
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
if (capabilities.subscriptionLimits) {
|
|
113
|
+
const limits = formatLimitsLeft(usage);
|
|
114
|
+
if (limits) {
|
|
115
|
+
lines.push(`<b>Limits left:</b> <code>${escapeHTML(limits)}</code>`);
|
|
116
|
+
}
|
|
113
117
|
}
|
|
114
118
|
return lines;
|
|
115
119
|
}
|
|
116
|
-
function renderAgentUsagePlain(info) {
|
|
120
|
+
function renderAgentUsagePlain(info, capabilities) {
|
|
121
|
+
if (!capabilities.usageStats) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
117
124
|
const lines = [];
|
|
118
125
|
if (info.contextUsage?.percent !== undefined && info.contextUsage.percent !== null) {
|
|
119
126
|
const contextWindow = info.contextUsage.contextWindow !== null && info.contextUsage.contextWindow !== undefined
|
|
@@ -131,7 +138,10 @@ function renderAgentUsagePlain(info) {
|
|
|
131
138
|
}
|
|
132
139
|
return lines;
|
|
133
140
|
}
|
|
134
|
-
function renderAgentUsageHTML(info) {
|
|
141
|
+
function renderAgentUsageHTML(info, capabilities) {
|
|
142
|
+
if (!capabilities.usageStats) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
135
145
|
const lines = [];
|
|
136
146
|
if (info.contextUsage?.percent !== undefined && info.contextUsage.percent !== null) {
|
|
137
147
|
const contextWindow = info.contextUsage.contextWindow !== null && info.contextUsage.contextWindow !== undefined
|
package/dist/session-registry.js
CHANGED
|
@@ -64,7 +64,7 @@ export class SessionRegistry {
|
|
|
64
64
|
agentId,
|
|
65
65
|
threadId: null,
|
|
66
66
|
workspace: previous?.workspace ?? this.config.workspace,
|
|
67
|
-
|
|
67
|
+
pinnedThreadIdsByAgent: previous?.pinnedThreadIdsByAgent,
|
|
68
68
|
updatedAt: Date.now(),
|
|
69
69
|
};
|
|
70
70
|
this.metadata.set(contextKey, next);
|
|
@@ -74,9 +74,12 @@ export class SessionRegistry {
|
|
|
74
74
|
updateMetadata(contextKey, session) {
|
|
75
75
|
const info = session.getInfo();
|
|
76
76
|
const previous = this.metadata.get(contextKey);
|
|
77
|
-
const
|
|
77
|
+
const agentId = info.agentId ?? "codex";
|
|
78
|
+
const previousPinnedByAgent = previous?.pinnedThreadIdsByAgent ?? {};
|
|
79
|
+
const pinnedThreadIds = previousPinnedByAgent[agentId] ?? previous?.pinnedThreadIds ?? [];
|
|
78
80
|
const next = {
|
|
79
81
|
contextKey,
|
|
82
|
+
agentId,
|
|
80
83
|
threadId: info.threadId,
|
|
81
84
|
workspace: info.workspace,
|
|
82
85
|
model: info.model,
|
|
@@ -84,49 +87,64 @@ export class SessionRegistry {
|
|
|
84
87
|
launchProfileId: info.nextLaunchProfileId ?? info.launchProfileId,
|
|
85
88
|
updatedAt: Date.now(),
|
|
86
89
|
};
|
|
87
|
-
if (info.agentId && info.agentId !== "codex") {
|
|
88
|
-
next.agentId = info.agentId;
|
|
89
|
-
}
|
|
90
90
|
if (info.sessionPath) {
|
|
91
91
|
next.sessionPath = info.sessionPath;
|
|
92
92
|
}
|
|
93
|
+
const nextPinnedByAgent = { ...previousPinnedByAgent };
|
|
93
94
|
if (pinnedThreadIds.length > 0) {
|
|
94
|
-
|
|
95
|
+
nextPinnedByAgent[agentId] = pinnedThreadIds;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
delete nextPinnedByAgent[agentId];
|
|
99
|
+
}
|
|
100
|
+
if (Object.keys(nextPinnedByAgent).length > 0) {
|
|
101
|
+
next.pinnedThreadIdsByAgent = nextPinnedByAgent;
|
|
95
102
|
}
|
|
96
103
|
this.metadata.set(contextKey, next);
|
|
97
104
|
this.persistMetadata();
|
|
98
105
|
}
|
|
99
106
|
pinThread(contextKey, threadId) {
|
|
100
107
|
const meta = this.metadata.get(contextKey) ?? this.createEmptyMetadata(contextKey);
|
|
101
|
-
const
|
|
108
|
+
const agentId = meta.agentId ?? this.config.defaultAgent ?? "codex";
|
|
109
|
+
const pinnedByAgent = meta.pinnedThreadIdsByAgent ?? {};
|
|
110
|
+
const pinned = new Set(pinnedByAgent[agentId] ?? meta.pinnedThreadIds ?? []);
|
|
102
111
|
pinned.add(threadId);
|
|
103
|
-
meta.
|
|
112
|
+
meta.pinnedThreadIdsByAgent = { ...pinnedByAgent, [agentId]: [...pinned] };
|
|
113
|
+
delete meta.pinnedThreadIds;
|
|
104
114
|
meta.updatedAt = Date.now();
|
|
105
115
|
this.metadata.set(contextKey, meta);
|
|
106
116
|
this.persistMetadata();
|
|
107
|
-
return meta.
|
|
117
|
+
return meta.pinnedThreadIdsByAgent[agentId] ?? [];
|
|
108
118
|
}
|
|
109
119
|
unpinThread(contextKey, threadId) {
|
|
110
120
|
const meta = this.metadata.get(contextKey) ?? this.createEmptyMetadata(contextKey);
|
|
111
|
-
|
|
121
|
+
const agentId = meta.agentId ?? this.config.defaultAgent ?? "codex";
|
|
122
|
+
const pinnedByAgent = meta.pinnedThreadIdsByAgent ?? {};
|
|
123
|
+
meta.pinnedThreadIdsByAgent = {
|
|
124
|
+
...pinnedByAgent,
|
|
125
|
+
[agentId]: (pinnedByAgent[agentId] ?? meta.pinnedThreadIds ?? []).filter((id) => id !== threadId),
|
|
126
|
+
};
|
|
127
|
+
delete meta.pinnedThreadIds;
|
|
112
128
|
meta.updatedAt = Date.now();
|
|
113
129
|
this.metadata.set(contextKey, meta);
|
|
114
130
|
this.persistMetadata();
|
|
115
|
-
return meta.
|
|
131
|
+
return meta.pinnedThreadIdsByAgent[agentId] ?? [];
|
|
116
132
|
}
|
|
117
133
|
listPinnedThreadIds(contextKey) {
|
|
118
|
-
|
|
134
|
+
const meta = this.metadata.get(contextKey);
|
|
135
|
+
const agentId = meta?.agentId ?? this.config.defaultAgent ?? "codex";
|
|
136
|
+
return [...(meta?.pinnedThreadIdsByAgent?.[agentId] ?? meta?.pinnedThreadIds ?? [])];
|
|
119
137
|
}
|
|
120
138
|
listContexts() {
|
|
121
139
|
return [...this.metadata.values()].sort((left, right) => right.updatedAt - left.updatedAt);
|
|
122
140
|
}
|
|
123
|
-
|
|
141
|
+
syncAllFromAgentState(options = {}) {
|
|
124
142
|
const results = [];
|
|
125
143
|
for (const [contextKey, session] of this.sessions.entries()) {
|
|
126
144
|
if (!(session.getInfo().capabilities ?? CODEX_AGENT_CAPABILITIES).externalActivity) {
|
|
127
145
|
continue;
|
|
128
146
|
}
|
|
129
|
-
const result = session.
|
|
147
|
+
const result = session.syncFromAgentState(options);
|
|
130
148
|
if (result.changed) {
|
|
131
149
|
this.updateMetadata(contextKey, session);
|
|
132
150
|
}
|
|
@@ -168,7 +186,10 @@ export class SessionRegistry {
|
|
|
168
186
|
}
|
|
169
187
|
for (const entry of data) {
|
|
170
188
|
if (entry.contextKey) {
|
|
171
|
-
this.metadata.set(entry.contextKey,
|
|
189
|
+
this.metadata.set(entry.contextKey, {
|
|
190
|
+
...entry,
|
|
191
|
+
agentId: entry.agentId ?? "codex",
|
|
192
|
+
});
|
|
172
193
|
}
|
|
173
194
|
}
|
|
174
195
|
}
|
|
@@ -179,6 +200,7 @@ export class SessionRegistry {
|
|
|
179
200
|
createEmptyMetadata(contextKey) {
|
|
180
201
|
return {
|
|
181
202
|
contextKey,
|
|
203
|
+
agentId: this.config.defaultAgent ?? "codex",
|
|
182
204
|
threadId: null,
|
|
183
205
|
workspace: this.config.workspace,
|
|
184
206
|
launchProfileId: this.config.defaultLaunchProfileId,
|
|
@@ -191,6 +213,9 @@ function resolveLaunchProfileId(config, meta) {
|
|
|
191
213
|
if (!meta?.launchProfileId) {
|
|
192
214
|
return undefined;
|
|
193
215
|
}
|
|
216
|
+
if (meta.agentId === "pi" || meta.agentId === "hermes" || meta.agentId === "openclaw" || meta.agentId === "claude-code") {
|
|
217
|
+
return meta.launchProfileId;
|
|
218
|
+
}
|
|
194
219
|
if (findLaunchProfile(config.launchProfiles, meta.launchProfileId)) {
|
|
195
220
|
return meta.launchProfileId;
|
|
196
221
|
}
|
package/dist/settings-service.js
CHANGED
|
@@ -3,6 +3,9 @@ import path from "node:path";
|
|
|
3
3
|
const SECRET_KEYS = new Set([
|
|
4
4
|
"TELEGRAM_BOT_TOKEN",
|
|
5
5
|
"CODEX_API_KEY",
|
|
6
|
+
"HERMES_API_KEY",
|
|
7
|
+
"OPENCLAW_GATEWAY_TOKEN",
|
|
8
|
+
"OPENCLAW_GATEWAY_PASSWORD",
|
|
6
9
|
"OPENAI_API_KEY",
|
|
7
10
|
"TELEGRAM_WEBHOOK_SECRET",
|
|
8
11
|
"NORDRELAY_DASHBOARD_TOKEN",
|
|
@@ -24,7 +27,10 @@ export const SETTING_DEFINITIONS = [
|
|
|
24
27
|
setting("TELEGRAM_WEBHOOK_SECRET", "Webhook secret", "Telegram", "secret", "Optional Telegram webhook secret token.", true),
|
|
25
28
|
setting("NORDRELAY_CODEX_ENABLED", "Enable Codex", "Agents", "boolean", "Allow Codex sessions.", true),
|
|
26
29
|
setting("NORDRELAY_PI_ENABLED", "Enable Pi", "Agents", "boolean", "Allow Pi sessions.", true),
|
|
27
|
-
setting("
|
|
30
|
+
setting("NORDRELAY_HERMES_ENABLED", "Enable Hermes", "Agents", "boolean", "Allow Hermes sessions through the Hermes API Server.", true),
|
|
31
|
+
setting("NORDRELAY_OPENCLAW_ENABLED", "Enable OpenClaw", "Agents", "boolean", "Allow OpenClaw sessions through the OpenClaw Gateway.", true),
|
|
32
|
+
setting("NORDRELAY_CLAUDE_CODE_ENABLED", "Enable Claude Code", "Agents", "boolean", "Allow Claude Code sessions through the Claude Agent SDK.", true),
|
|
33
|
+
setting("NORDRELAY_DEFAULT_AGENT", "Default agent", "Agents", "string", "codex, pi, hermes, openclaw, or claude-code.", true, ["codex", "pi", "hermes", "openclaw", "claude-code"]),
|
|
28
34
|
setting("CODEX_API_KEY", "Codex API key", "Codex", "secret", "Optional Codex SDK API key.", true),
|
|
29
35
|
setting("CODEX_CLI_PATH", "Codex CLI path", "Codex", "string", "Optional explicit Codex executable path.", true),
|
|
30
36
|
setting("CODEX_USE_BUNDLED_CLI", "Use bundled Codex CLI", "Codex", "boolean", "Force SDK-bundled CLI instead of host CLI.", true),
|
|
@@ -41,6 +47,31 @@ export const SETTING_DEFINITIONS = [
|
|
|
41
47
|
setting("PI_SESSION_DIR", "Pi session dir", "Pi", "string", "Optional Pi session directory.", true),
|
|
42
48
|
setting("PI_DEFAULT_MODEL", "Default Pi model", "Pi", "string", "Default Pi model slug.", false),
|
|
43
49
|
setting("PI_DEFAULT_THINKING", "Default Pi thinking", "Pi", "string", "off, minimal, low, medium, high, or xhigh.", false, ["off", "minimal", "low", "medium", "high", "xhigh"]),
|
|
50
|
+
setting("PI_DEFAULT_PROFILE", "Default Pi profile", "Pi", "string", "default, readonly, no-tools, offline, or safe-offline.", true, ["default", "readonly", "no-tools", "offline", "safe-offline"]),
|
|
51
|
+
setting("HERMES_CLI_PATH", "Hermes CLI path", "Hermes", "string", "Optional Hermes executable path.", true),
|
|
52
|
+
setting("HERMES_HOME", "Hermes home", "Hermes", "string", "Optional Hermes home directory. Defaults to ~/.hermes.", true),
|
|
53
|
+
setting("HERMES_STATE_DB_PATH", "Hermes state DB path", "Hermes", "string", "Optional explicit Hermes state.db path.", true),
|
|
54
|
+
setting("HERMES_API_BASE_URL", "Hermes API base URL", "Hermes", "string", "Hermes API Server base URL.", true),
|
|
55
|
+
setting("HERMES_API_KEY", "Hermes API key", "Hermes", "secret", "Bearer token for the Hermes API Server.", true),
|
|
56
|
+
setting("HERMES_DEFAULT_MODEL", "Default Hermes model", "Hermes", "string", "Default model label sent to Hermes API runs.", false),
|
|
57
|
+
setting("HERMES_DEFAULT_REASONING", "Default Hermes reasoning", "Hermes", "string", "none, minimal, low, medium, high, or xhigh.", false, ["none", "minimal", "low", "medium", "high", "xhigh"]),
|
|
58
|
+
setting("HERMES_DEFAULT_PROFILE", "Default Hermes profile", "Hermes", "string", "default, safe, readonly, or yolo.", true, ["default", "safe", "readonly", "yolo"]),
|
|
59
|
+
setting("OPENCLAW_CLI_PATH", "OpenClaw CLI path", "OpenClaw", "string", "Optional OpenClaw executable path.", true),
|
|
60
|
+
setting("OPENCLAW_GATEWAY_URL", "OpenClaw Gateway URL", "OpenClaw", "string", "OpenClaw Gateway WebSocket URL.", true),
|
|
61
|
+
setting("OPENCLAW_GATEWAY_TOKEN", "OpenClaw Gateway token", "OpenClaw", "secret", "Shared-secret token for the OpenClaw Gateway.", true),
|
|
62
|
+
setting("OPENCLAW_GATEWAY_PASSWORD", "OpenClaw Gateway password", "OpenClaw", "secret", "Shared-secret password for the OpenClaw Gateway.", true),
|
|
63
|
+
setting("OPENCLAW_AGENT_ID", "OpenClaw agent ID", "OpenClaw", "string", "Configured OpenClaw agent id, for example main or work.", false),
|
|
64
|
+
setting("OPENCLAW_HOME", "OpenClaw home", "OpenClaw", "string", "Optional OpenClaw home directory. Defaults to ~/.openclaw.", true),
|
|
65
|
+
setting("OPENCLAW_STATE_DIR", "OpenClaw state dir", "OpenClaw", "string", "Optional OpenClaw state directory.", true),
|
|
66
|
+
setting("OPENCLAW_DEFAULT_MODEL", "Default OpenClaw model", "OpenClaw", "string", "Default OpenClaw model id.", false),
|
|
67
|
+
setting("OPENCLAW_DEFAULT_THINKING", "Default OpenClaw thinking", "OpenClaw", "string", "off, minimal, low, medium, high, or xhigh.", false, ["off", "minimal", "low", "medium", "high", "xhigh"]),
|
|
68
|
+
setting("OPENCLAW_DEFAULT_PROFILE", "Default OpenClaw profile", "OpenClaw", "string", "default, safe, readonly, local, or deliver.", true, ["default", "safe", "readonly", "local", "deliver"]),
|
|
69
|
+
setting("CLAUDE_CODE_CLI_PATH", "Claude Code CLI path", "Claude Code", "string", "Optional Claude Code executable path. Defaults to claude on PATH or the SDK bundled runtime.", true),
|
|
70
|
+
setting("CLAUDE_CONFIG_DIR", "Claude config dir", "Claude Code", "string", "Optional Claude config directory. Defaults to ~/.claude.", true),
|
|
71
|
+
setting("CLAUDE_CODE_DEFAULT_MODEL", "Default Claude Code model", "Claude Code", "string", "Default Claude Code model alias or model id.", false),
|
|
72
|
+
setting("CLAUDE_CODE_DEFAULT_EFFORT", "Default Claude Code effort", "Claude Code", "string", "off, low, medium, high, or xhigh.", false, ["off", "low", "medium", "high", "xhigh"]),
|
|
73
|
+
setting("CLAUDE_CODE_DEFAULT_PROFILE", "Default Claude Code profile", "Claude Code", "string", "default, accept-edits, plan, readonly, no-tools, or bypass-permissions.", true, ["default", "accept-edits", "plan", "readonly", "no-tools", "bypass-permissions"]),
|
|
74
|
+
setting("CLAUDE_CODE_MAX_TURNS", "Claude Code max turns", "Claude Code", "number", "Maximum agentic turns for each Claude Code prompt.", false),
|
|
44
75
|
setting("CONNECTOR_LOG_FORMAT", "Log format", "Operations", "string", "text or json.", true, ["text", "json"]),
|
|
45
76
|
setting("TOOL_VERBOSITY", "Tool verbosity", "Operations", "string", "all, summary, errors-only, or none.", false, ["all", "summary", "errors-only", "none"]),
|
|
46
77
|
setting("SHOW_TURN_TOKEN_USAGE", "Show turn token usage", "Operations", "boolean", "Append per-turn token usage.", false),
|
|
@@ -88,15 +119,15 @@ export class SettingsService {
|
|
|
88
119
|
constructor(envPath) {
|
|
89
120
|
this.envPath = envPath;
|
|
90
121
|
}
|
|
91
|
-
async snapshot(env = process.env) {
|
|
122
|
+
async snapshot(env = process.env, activeValues = {}) {
|
|
92
123
|
const parsed = await readEnvFile(this.envPath);
|
|
93
124
|
const settings = SETTING_DEFINITIONS.map((definition) => {
|
|
94
125
|
const configuredValue = parsed[definition.key];
|
|
95
|
-
const effectiveValue = configuredValue ?? env[definition.key] ?? "";
|
|
126
|
+
const effectiveValue = configuredValue ?? activeValues[definition.key] ?? env[definition.key] ?? "";
|
|
96
127
|
const masked = SECRET_KEYS.has(definition.key) && Boolean(effectiveValue);
|
|
97
128
|
return {
|
|
98
129
|
...definition,
|
|
99
|
-
value:
|
|
130
|
+
value: configuredValue === undefined ? "" : SECRET_KEYS.has(definition.key) && configuredValue ? maskSecret(configuredValue) : configuredValue,
|
|
100
131
|
effectiveValue: masked ? maskSecret(effectiveValue) : effectiveValue,
|
|
101
132
|
configured: configuredValue !== undefined,
|
|
102
133
|
masked,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const DASHBOARD_PAGES = [
|
|
2
|
+
{ id: "overview", label: "Overview" },
|
|
3
|
+
{ id: "chat", label: "Chat" },
|
|
4
|
+
{ id: "sessions", label: "Sessions" },
|
|
5
|
+
{ id: "queue", label: "Queue" },
|
|
6
|
+
{ id: "tasks", label: "Tasks" },
|
|
7
|
+
{ id: "activity", label: "Activity" },
|
|
8
|
+
{ id: "artifacts", label: "Artifacts" },
|
|
9
|
+
{ id: "adapters", label: "Adapters" },
|
|
10
|
+
{ id: "access", label: "Access" },
|
|
11
|
+
{ id: "version", label: "Version" },
|
|
12
|
+
{ id: "settings", label: "Settings" },
|
|
13
|
+
{ id: "logs", label: "Logs" },
|
|
14
|
+
{ id: "diagnostics", label: "Diagnostics" },
|
|
15
|
+
];
|
|
16
|
+
export function renderDashboardNav(activePage = "overview") {
|
|
17
|
+
return DASHBOARD_PAGES.map((page) => `<button data-page="${page.id}"${page.id === activePage ? ' class="active"' : ""}>${page.label}</button>`).join("\n ");
|
|
18
|
+
}
|