@formthefog/stratus 2026.2.24 → 2026.3.20
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/.github/sentinel/action.yml +100 -0
- package/.github/sentinel/dist/codebase.d.ts +3 -0
- package/.github/sentinel/dist/codebase.d.ts.map +1 -0
- package/.github/sentinel/dist/context.d.ts +6 -0
- package/.github/sentinel/dist/context.d.ts.map +1 -0
- package/.github/sentinel/dist/fixer.d.ts +6 -0
- package/.github/sentinel/dist/fixer.d.ts.map +1 -0
- package/.github/sentinel/dist/index.d.ts +1 -0
- package/.github/sentinel/dist/index.d.ts.map +1 -0
- package/.github/sentinel/dist/index.js +68808 -0
- package/.github/sentinel/dist/index.js.map +1 -0
- package/.github/sentinel/dist/licenses.txt +1152 -0
- package/.github/sentinel/dist/models/anthropic.d.ts +26 -0
- package/.github/sentinel/dist/models/anthropic.d.ts.map +1 -0
- package/.github/sentinel/dist/models/openai.d.ts +26 -0
- package/.github/sentinel/dist/models/openai.d.ts.map +1 -0
- package/.github/sentinel/dist/models/openrouter.d.ts +31 -0
- package/.github/sentinel/dist/models/openrouter.d.ts.map +1 -0
- package/.github/sentinel/dist/models/types.d.ts +37 -0
- package/.github/sentinel/dist/models/types.d.ts.map +1 -0
- package/.github/sentinel/dist/orchestrator.d.ts +3 -0
- package/.github/sentinel/dist/orchestrator.d.ts.map +1 -0
- package/.github/sentinel/dist/policy.d.ts +15 -0
- package/.github/sentinel/dist/policy.d.ts.map +1 -0
- package/.github/sentinel/dist/reporter.d.ts +8 -0
- package/.github/sentinel/dist/reporter.d.ts.map +1 -0
- package/.github/sentinel/dist/responder.d.ts +6 -0
- package/.github/sentinel/dist/responder.d.ts.map +1 -0
- package/.github/sentinel/dist/router.d.ts +2 -0
- package/.github/sentinel/dist/router.d.ts.map +1 -0
- package/.github/sentinel/dist/schemas/config.d.ts +195 -0
- package/.github/sentinel/dist/schemas/config.d.ts.map +1 -0
- package/.github/sentinel/dist/schemas/fix.d.ts +130 -0
- package/.github/sentinel/dist/schemas/fix.d.ts.map +1 -0
- package/.github/sentinel/dist/schemas/review.d.ts +275 -0
- package/.github/sentinel/dist/schemas/review.d.ts.map +1 -0
- package/.github/sentinel/dist/sourcemap-register.js +1 -0
- package/.github/sentinel/dist/subway.d.ts +31 -0
- package/.github/sentinel/dist/subway.d.ts.map +1 -0
- package/.github/sentinel/dist/types.d.ts +210 -0
- package/.github/sentinel/dist/types.d.ts.map +1 -0
- package/.github/sentinel/package-lock.json +2389 -0
- package/.github/sentinel/package.json +29 -0
- package/.github/sentinel/src/codebase.ts +265 -0
- package/.github/sentinel/src/context.ts +182 -0
- package/.github/sentinel/src/fixer.ts +353 -0
- package/.github/sentinel/src/index.ts +263 -0
- package/.github/sentinel/src/models/anthropic.ts +244 -0
- package/.github/sentinel/src/models/openai.ts +242 -0
- package/.github/sentinel/src/models/openrouter.ts +319 -0
- package/.github/sentinel/src/models/types.ts +35 -0
- package/.github/sentinel/src/orchestrator.ts +287 -0
- package/.github/sentinel/src/policy.ts +133 -0
- package/.github/sentinel/src/reporter.ts +666 -0
- package/.github/sentinel/src/responder.ts +156 -0
- package/.github/sentinel/src/router.ts +308 -0
- package/.github/sentinel/src/schemas/config.ts +84 -0
- package/.github/sentinel/src/schemas/fix.ts +44 -0
- package/.github/sentinel/src/schemas/review.ts +73 -0
- package/.github/sentinel/src/subway.ts +250 -0
- package/.github/sentinel/src/types.ts +234 -0
- package/.github/sentinel/tsconfig.json +19 -0
- package/.github/sentinel.yml +34 -0
- package/.github/workflows/sentinel.yml +55 -0
- package/README.md +88 -102
- package/SECURITY.md +21 -10
- package/TROUBLESHOOTING.md +2 -2
- package/index.ts +255 -114
- package/openclaw.plugin.json +50 -26
- package/package.json +1 -1
- package/skills/stratus-info/SKILL.md +70 -10
- package/src/client.ts +78 -18
- package/src/config.ts +29 -8
- package/src/setup.ts +82 -61
- package/src/types.ts +11 -0
package/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ import type { StratusPluginConfig } from "./src/types.js";
|
|
|
4
4
|
import { createStratusClient } from "./src/client.js";
|
|
5
5
|
import { StratusConfigSchema } from "./src/config.js";
|
|
6
6
|
import { setupStratus } from "./src/setup.js";
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
7
10
|
|
|
8
11
|
const PROVIDER_ID = "stratus";
|
|
9
12
|
const PROVIDER_LABEL = "Stratus";
|
|
@@ -26,59 +29,108 @@ function buildModelDefinition(params: {
|
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for (const size of STRATUS_SIZES) {
|
|
56
|
-
const sizeLabel = size.charAt(0).toUpperCase() + size.slice(1);
|
|
57
|
-
|
|
58
|
-
for (const llm of OPENAI_MODELS) {
|
|
59
|
-
models.push(
|
|
60
|
-
buildModelDefinition({
|
|
61
|
-
id: `stratus-x1ac-${size}-${llm.id}`,
|
|
62
|
-
name: `Stratus X1AC ${sizeLabel} (${llm.name})`,
|
|
63
|
-
contextWindow: llm.context,
|
|
64
|
-
maxTokens: llm.tokens,
|
|
65
|
-
}),
|
|
66
|
-
);
|
|
67
|
-
}
|
|
32
|
+
// Model metadata for naming/context/tokens (fallback if API doesn't provide details)
|
|
33
|
+
const MODEL_METADATA: Record<string, { name: string; context: number; tokens: number }> = {
|
|
34
|
+
"gpt-4o": { name: "GPT-4o", context: 128000, tokens: 8192 },
|
|
35
|
+
"gpt-4o-mini": { name: "GPT-4o Mini", context: 128000, tokens: 8192 },
|
|
36
|
+
"gpt-4-turbo": { name: "GPT-4 Turbo", context: 128000, tokens: 4096 },
|
|
37
|
+
"gpt-4": { name: "GPT-4", context: 8192, tokens: 4096 },
|
|
38
|
+
"gpt-3.5-turbo": { name: "GPT-3.5 Turbo", context: 16385, tokens: 4096 },
|
|
39
|
+
"claude-sonnet-4-6": { name: "Claude 4.6 Sonnet", context: 200000, tokens: 8192 },
|
|
40
|
+
"claude-opus-4-6": { name: "Claude 4.6 Opus", context: 200000, tokens: 8192 },
|
|
41
|
+
"claude-sonnet-4-5": { name: "Claude 4.5 Sonnet", context: 200000, tokens: 8192 },
|
|
42
|
+
"claude-opus-4-5": { name: "Claude 4.5 Opus", context: 200000, tokens: 8192 },
|
|
43
|
+
"claude-haiku-4-5": { name: "Claude 4.5 Haiku", context: 200000, tokens: 8192 },
|
|
44
|
+
"claude-sonnet-4": { name: "Claude 4 Sonnet", context: 200000, tokens: 8192 },
|
|
45
|
+
"claude-opus-4-1": { name: "Claude 4.1 Opus", context: 200000, tokens: 8192 },
|
|
46
|
+
"claude-opus-4": { name: "Claude 4 Opus", context: 200000, tokens: 8192 },
|
|
47
|
+
"claude-3-7-sonnet": { name: "Claude 3.7 Sonnet", context: 200000, tokens: 8192 },
|
|
48
|
+
"claude-3-5-sonnet": { name: "Claude 3.5 Sonnet", context: 200000, tokens: 8192 },
|
|
49
|
+
"claude-3-opus": { name: "Claude 3 Opus", context: 200000, tokens: 4096 },
|
|
50
|
+
"claude-3-sonnet": { name: "Claude 3 Sonnet", context: 200000, tokens: 4096 },
|
|
51
|
+
"claude-3-haiku": { name: "Claude 3 Haiku", context: 200000, tokens: 4096 },
|
|
52
|
+
"gemini-2.0-flash": { name: "Gemini 2.0 Flash", context: 1048576, tokens: 8192 },
|
|
53
|
+
"gemini-1.5-pro": { name: "Gemini 1.5 Pro", context: 2097152, tokens: 8192 },
|
|
54
|
+
"gemini-1.5-flash": { name: "Gemini 1.5 Flash", context: 1048576, tokens: 8192 },
|
|
55
|
+
"gemini-pro": { name: "Gemini Pro", context: 32768, tokens: 8192 },
|
|
56
|
+
};
|
|
68
57
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
58
|
+
function generateModelName(modelId: string): string {
|
|
59
|
+
// Parse model ID like "stratus-x1ac-base-claude-sonnet-4-5"
|
|
60
|
+
const parts = modelId.split("-");
|
|
61
|
+
|
|
62
|
+
// Extract size (small/base/large/xl/huge)
|
|
63
|
+
let size = "";
|
|
64
|
+
let baseModelId = "";
|
|
65
|
+
|
|
66
|
+
if (parts.length >= 4 && parts[0] === "stratus" && parts[1] === "x1ac") {
|
|
67
|
+
size = parts[2];
|
|
68
|
+
baseModelId = parts.slice(3).join("-");
|
|
69
|
+
} else {
|
|
70
|
+
// Fallback for non-standard format
|
|
71
|
+
return modelId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sizeLabel = size.charAt(0).toUpperCase() + size.slice(1);
|
|
75
|
+
const metadata = MODEL_METADATA[baseModelId];
|
|
76
|
+
|
|
77
|
+
if (metadata) {
|
|
78
|
+
return `Stratus X1AC ${sizeLabel} (${metadata.name})`;
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
// Fallback: capitalize base model
|
|
82
|
+
return `Stratus X1AC ${sizeLabel} (${baseModelId})`;
|
|
83
|
+
}
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
async function generateAllModels(apiKey?: string, baseUrl?: string) {
|
|
86
|
+
const root = (baseUrl || DEFAULT_BASE_URL).replace(/\/v\d+\/?$/, "");
|
|
87
|
+
try {
|
|
88
|
+
const url = `${root}/v1/models`;
|
|
89
|
+
const headers: Record<string, string> = {};
|
|
90
|
+
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
91
|
+
|
|
92
|
+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(5000) });
|
|
93
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
94
|
+
const data = await response.json() as { data: Array<{ id: string }> };
|
|
95
|
+
|
|
96
|
+
return data.data.map((model) => {
|
|
97
|
+
const modelId = model.id;
|
|
98
|
+
const baseModelId = modelId.replace(/^stratus-x1ac-(small|base|large|xl|huge)-/, "");
|
|
99
|
+
const metadata = MODEL_METADATA[baseModelId];
|
|
100
|
+
return buildModelDefinition({
|
|
101
|
+
id: modelId,
|
|
102
|
+
name: generateModelName(modelId),
|
|
103
|
+
contextWindow: metadata?.context || 128000,
|
|
104
|
+
maxTokens: metadata?.tokens || 8192,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error("[stratus] Failed to fetch models from API:", error);
|
|
109
|
+
return [
|
|
110
|
+
buildModelDefinition({
|
|
111
|
+
id: "stratus-x1ac-base-claude-sonnet-4-5",
|
|
112
|
+
name: "Stratus X1AC Base (Claude 4.5 Sonnet)",
|
|
113
|
+
contextWindow: 200000,
|
|
114
|
+
maxTokens: 8192,
|
|
115
|
+
}),
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function updateStoredModels(models: ReturnType<typeof buildModelDefinition>[]) {
|
|
121
|
+
const configPath = join(homedir(), ".openclaw", "openclaw.json");
|
|
122
|
+
if (!existsSync(configPath)) return;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
126
|
+
const config = JSON.parse(raw);
|
|
127
|
+
if (!config?.models?.providers?.stratus) return;
|
|
128
|
+
|
|
129
|
+
config.models.providers.stratus.models = models;
|
|
130
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
131
|
+
} catch {
|
|
132
|
+
// Silent fail — stale models are better than a crash
|
|
133
|
+
}
|
|
82
134
|
}
|
|
83
135
|
|
|
84
136
|
const stratusPlugin = {
|
|
@@ -98,15 +150,13 @@ const stratusPlugin = {
|
|
|
98
150
|
auth: [
|
|
99
151
|
{
|
|
100
152
|
id: "api-key",
|
|
101
|
-
label: "API Key",
|
|
153
|
+
label: "API Key (optional — Formation pool available)",
|
|
102
154
|
kind: "api_key",
|
|
103
155
|
run: async (ctx) => {
|
|
104
156
|
const apiKey = await ctx.prompter.text({
|
|
105
|
-
message: "Enter your Stratus API key:",
|
|
157
|
+
message: "Enter your Stratus API key (leave blank for Formation pool — zero-config):",
|
|
106
158
|
validate: (val: string) => {
|
|
107
|
-
if (!val.trim())
|
|
108
|
-
return "API key is required";
|
|
109
|
-
}
|
|
159
|
+
if (!val.trim()) return undefined;
|
|
110
160
|
if (!val.startsWith("stratus_sk_")) {
|
|
111
161
|
return "API key must start with 'stratus_sk_'";
|
|
112
162
|
}
|
|
@@ -114,36 +164,30 @@ const stratusPlugin = {
|
|
|
114
164
|
},
|
|
115
165
|
});
|
|
116
166
|
|
|
117
|
-
const
|
|
167
|
+
const usingPool = !apiKey?.trim();
|
|
168
|
+
const resolvedKey = usingPool ? undefined : apiKey;
|
|
169
|
+
const profileId = `${PROVIDER_ID}:${usingPool ? "formation-pool" : "default"}`;
|
|
170
|
+
|
|
171
|
+
const models = await generateAllModels(resolvedKey, DEFAULT_BASE_URL);
|
|
172
|
+
|
|
173
|
+
const credential = usingPool
|
|
174
|
+
? { type: "formation_pool" as const, provider: PROVIDER_ID }
|
|
175
|
+
: { type: "api_key" as const, provider: PROVIDER_ID, key: resolvedKey! };
|
|
176
|
+
|
|
177
|
+
const authMode = usingPool
|
|
178
|
+
? "Formation pool (zero-config, 25% markup)"
|
|
179
|
+
: "BYOK (no markup)";
|
|
118
180
|
|
|
119
181
|
return {
|
|
120
|
-
profiles: [
|
|
121
|
-
{
|
|
122
|
-
profileId,
|
|
123
|
-
credential: {
|
|
124
|
-
type: "api_key",
|
|
125
|
-
provider: PROVIDER_ID,
|
|
126
|
-
key: apiKey,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
],
|
|
182
|
+
profiles: [{ profileId, credential }],
|
|
130
183
|
configPatch: {
|
|
131
184
|
models: {
|
|
132
185
|
providers: {
|
|
133
186
|
[PROVIDER_ID]: {
|
|
134
187
|
baseUrl: DEFAULT_BASE_URL,
|
|
135
|
-
apiKey:
|
|
188
|
+
...(resolvedKey ? { apiKey: resolvedKey } : {}),
|
|
136
189
|
api: "openai-completions",
|
|
137
|
-
models:
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
agents: {
|
|
142
|
-
defaults: {
|
|
143
|
-
models: {
|
|
144
|
-
[`${PROVIDER_ID}/stratus-x1ac-base-claude-sonnet-4-5`]: {
|
|
145
|
-
alias: "stratus",
|
|
146
|
-
},
|
|
190
|
+
models: models,
|
|
147
191
|
},
|
|
148
192
|
},
|
|
149
193
|
},
|
|
@@ -152,20 +196,26 @@ const stratusPlugin = {
|
|
|
152
196
|
notes: [
|
|
153
197
|
"🌊 Stratus X1-AC: World Model + Collective Intelligence System",
|
|
154
198
|
"",
|
|
199
|
+
`🔒 Auth: ${authMode}`,
|
|
200
|
+
"",
|
|
155
201
|
"NOT just an LLM wrapper — this is predictive planning:",
|
|
156
202
|
" Traditional agents: see → think → act → see again",
|
|
157
203
|
" Stratus agents: see → simulate outcomes → pick best → act",
|
|
158
204
|
"",
|
|
159
205
|
"Architecture:",
|
|
160
206
|
" • World Model (X1): Predicts future states from actions (JEPA-based)",
|
|
161
|
-
" • Policy Head (X1-AC):
|
|
207
|
+
" • Policy Head v3 (X1-AC): 94.4% accuracy, brain-guided action sequencing",
|
|
162
208
|
" • Collective Network: Agents learn from each other in real-time",
|
|
163
209
|
"",
|
|
210
|
+
`Loaded ${models.length} models dynamically from Stratus API`,
|
|
211
|
+
" 2050+ models via OpenRouter — OpenAI, Anthropic, Google, and more",
|
|
212
|
+
" BYOK support: pass your own provider keys to bypass Formation pool",
|
|
213
|
+
"",
|
|
164
214
|
"Available Tools:",
|
|
165
215
|
" • stratus_embeddings() - 768-dim semantic state embeddings",
|
|
166
|
-
" • stratus_rollout() - Multi-step action sequence planning",
|
|
216
|
+
" • stratus_rollout() - Multi-step action sequence planning (Policy Head v3)",
|
|
167
217
|
"",
|
|
168
|
-
"
|
|
218
|
+
"Commands: /stratus setup | /stratus verify | /stratus models",
|
|
169
219
|
"Technical docs: https://stratus.run/docs/technical-overview",
|
|
170
220
|
],
|
|
171
221
|
};
|
|
@@ -174,6 +224,42 @@ const stratusPlugin = {
|
|
|
174
224
|
],
|
|
175
225
|
});
|
|
176
226
|
|
|
227
|
+
api.on("gateway_start", async () => {
|
|
228
|
+
const apiKey = pluginConfig.apiKey || process.env.STRATUS_API_KEY;
|
|
229
|
+
try {
|
|
230
|
+
const models = await generateAllModels(apiKey, pluginConfig.baseUrl);
|
|
231
|
+
await updateStoredModels(models);
|
|
232
|
+
} catch {
|
|
233
|
+
// Silent fail — don't disrupt gateway startup
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (apiKey) {
|
|
237
|
+
try {
|
|
238
|
+
const authPath = join(homedir(), ".openclaw", "agents", "main", "agent", "auth-profiles.json");
|
|
239
|
+
let authConfig: any = { version: 1, profiles: {}, lastGood: {}, usageStats: {} };
|
|
240
|
+
if (existsSync(authPath)) {
|
|
241
|
+
authConfig = JSON.parse(readFileSync(authPath, "utf-8"));
|
|
242
|
+
}
|
|
243
|
+
const existingProfile = authConfig.profiles["stratus:default"];
|
|
244
|
+
if (!existingProfile || existingProfile.key !== apiKey) {
|
|
245
|
+
authConfig.profiles["stratus:default"] = {
|
|
246
|
+
type: "api_key",
|
|
247
|
+
provider: "stratus",
|
|
248
|
+
key: apiKey,
|
|
249
|
+
};
|
|
250
|
+
authConfig.lastGood.stratus = "stratus:default";
|
|
251
|
+
const authDir = join(homedir(), ".openclaw", "agents", "main", "agent");
|
|
252
|
+
if (!existsSync(authDir)) {
|
|
253
|
+
mkdirSync(authDir, { recursive: true });
|
|
254
|
+
}
|
|
255
|
+
writeFileSync(authPath, JSON.stringify(authConfig, null, 2));
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
// Silent fail — don't disrupt gateway startup
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
177
263
|
if (!pluginConfig.tools?.embeddings?.enabled && !pluginConfig.tools?.rollout?.enabled) {
|
|
178
264
|
return;
|
|
179
265
|
}
|
|
@@ -285,11 +371,24 @@ const stratusPlugin = {
|
|
|
285
371
|
? `Successfully planned ${steps.length} steps to reach goal`
|
|
286
372
|
: `Planning incomplete (${steps.length} steps generated)`;
|
|
287
373
|
|
|
374
|
+
const meta = response.metadata;
|
|
375
|
+
const metaLines: string[] = [];
|
|
376
|
+
if (meta?.brain_confidence != null) {
|
|
377
|
+
metaLines.push(`Brain confidence: ${(meta.brain_confidence * 100).toFixed(1)}%`);
|
|
378
|
+
}
|
|
379
|
+
if (meta?.brain_goal_proximity != null) {
|
|
380
|
+
metaLines.push(`Goal proximity: ${(meta.brain_goal_proximity * 100).toFixed(1)}%`);
|
|
381
|
+
}
|
|
382
|
+
if (meta?.key_source) {
|
|
383
|
+
metaLines.push(`Key source: ${meta.key_source}${meta.formation_markup_applied ? ` (${meta.formation_markup_applied * 100}% markup)` : ""}`);
|
|
384
|
+
}
|
|
385
|
+
const metaText = metaLines.length > 0 ? `\n\n${metaLines.join("\n")}` : "";
|
|
386
|
+
|
|
288
387
|
return {
|
|
289
388
|
content: [
|
|
290
389
|
{
|
|
291
390
|
type: "text",
|
|
292
|
-
text: `${summary}\nGoal: ${response.goal}\n\nSteps:\n${stepText}`,
|
|
391
|
+
text: `${summary}\nGoal: ${response.goal}\n\nSteps:\n${stepText}${metaText}`,
|
|
293
392
|
},
|
|
294
393
|
],
|
|
295
394
|
details: response,
|
|
@@ -312,8 +411,10 @@ const stratusPlugin = {
|
|
|
312
411
|
const subcommand = (tokens[0] || "help").toLowerCase();
|
|
313
412
|
|
|
314
413
|
if (subcommand === "setup") {
|
|
315
|
-
|
|
316
|
-
const result =
|
|
414
|
+
const keyArg = tokens[1];
|
|
415
|
+
const result = keyArg
|
|
416
|
+
? await setupStratus({ apiKey: keyArg, silent: true })
|
|
417
|
+
: await setupStratus(ctx.prompter);
|
|
317
418
|
|
|
318
419
|
if (result.success) {
|
|
319
420
|
console.log(`\n✅ ${result.message}\n`);
|
|
@@ -327,52 +428,92 @@ const stratusPlugin = {
|
|
|
327
428
|
}
|
|
328
429
|
process.exit(1);
|
|
329
430
|
}
|
|
330
|
-
return { text: "" };
|
|
431
|
+
return { text: "" };
|
|
432
|
+
} else if (subcommand === "models") {
|
|
433
|
+
const apiKey = pluginConfig.apiKey || process.env.STRATUS_API_KEY;
|
|
434
|
+
const models = await generateAllModels(apiKey, pluginConfig.baseUrl);
|
|
435
|
+
|
|
436
|
+
await updateStoredModels(models);
|
|
437
|
+
|
|
438
|
+
const sizes = ["small", "base", "large", "xl", "huge"];
|
|
439
|
+
const grouped = sizes
|
|
440
|
+
.map((size) => {
|
|
441
|
+
const sizeModels = models.filter(
|
|
442
|
+
(m) => m.id.includes(`-x1ac-${size}-`) || m.id.endsWith(`-x1ac-${size}`)
|
|
443
|
+
);
|
|
444
|
+
if (sizeModels.length === 0) return null;
|
|
445
|
+
return ` ${size.toUpperCase()} (${sizeModels.length})\n` +
|
|
446
|
+
sizeModels.map((m) => ` • ${m.id}`).join("\n");
|
|
447
|
+
})
|
|
448
|
+
.filter(Boolean)
|
|
449
|
+
.join("\n\n");
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
text:
|
|
453
|
+
`Stratus Models (${models.length} total)\n` +
|
|
454
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n` +
|
|
455
|
+
grouped +
|
|
456
|
+
`\n\nConfig synced. Run /models to see the full list.`,
|
|
457
|
+
};
|
|
331
458
|
} else if (subcommand === "verify") {
|
|
332
|
-
// Run verify
|
|
333
459
|
console.log("🔍 Verifying Stratus configuration...\n");
|
|
334
460
|
|
|
335
|
-
|
|
461
|
+
const hasKey = !!(pluginConfig.apiKey || process.env.STRATUS_API_KEY);
|
|
336
462
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
console.log(" ✓ STRATUS_API_KEY is set");
|
|
463
|
+
console.log("1️⃣ Auth mode:");
|
|
464
|
+
if (hasKey) {
|
|
465
|
+
console.log(" ✓ BYOK — using STRATUS_API_KEY (no markup)");
|
|
341
466
|
} else {
|
|
342
|
-
console.log("
|
|
343
|
-
|
|
467
|
+
console.log(" ✓ Formation pool — zero-config (25% markup)");
|
|
468
|
+
console.log(" 💡 Set STRATUS_API_KEY to remove markup");
|
|
344
469
|
}
|
|
345
470
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
471
|
+
console.log("\n2️⃣ API connectivity...");
|
|
472
|
+
try {
|
|
473
|
+
const models = await generateAllModels(
|
|
474
|
+
hasKey ? (pluginConfig.apiKey || process.env.STRATUS_API_KEY) : undefined,
|
|
475
|
+
pluginConfig.baseUrl,
|
|
476
|
+
);
|
|
477
|
+
console.log(` ✓ Connected — ${models.length} models available`);
|
|
478
|
+
} catch {
|
|
479
|
+
console.log(" ⚠ Could not reach API (may still work at runtime)");
|
|
353
480
|
}
|
|
354
481
|
|
|
355
|
-
console.log("\
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
482
|
+
console.log("\n3️⃣ Inline BYOK keys:");
|
|
483
|
+
const envKeys = [
|
|
484
|
+
["OPENAI_API_KEY", process.env.OPENAI_API_KEY],
|
|
485
|
+
["ANTHROPIC_API_KEY", process.env.ANTHROPIC_API_KEY],
|
|
486
|
+
["GOOGLE_API_KEY", process.env.GOOGLE_API_KEY],
|
|
487
|
+
] as const;
|
|
488
|
+
let anyInline = false;
|
|
489
|
+
for (const [name, val] of envKeys) {
|
|
490
|
+
if (val) {
|
|
491
|
+
console.log(` ✓ ${name} detected (BYOK passthrough)`);
|
|
492
|
+
anyInline = true;
|
|
493
|
+
}
|
|
366
494
|
}
|
|
367
|
-
|
|
495
|
+
if (!anyInline) {
|
|
496
|
+
console.log(" – None set (optional — Formation pool covers all providers)");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
console.log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
500
|
+
console.log("✅ Stratus is ready!");
|
|
501
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
|
|
502
|
+
console.log("🎯 Try it out:");
|
|
503
|
+
console.log(" openclaw agent 'Hello Stratus!' --model stratus\n");
|
|
504
|
+
return { text: "" };
|
|
368
505
|
} else {
|
|
369
|
-
// Show help
|
|
370
506
|
return {
|
|
371
507
|
text:
|
|
372
508
|
"🌊 Stratus X1-AC Plugin\n\n" +
|
|
373
509
|
"Commands:\n" +
|
|
374
|
-
" /stratus setup
|
|
375
|
-
" /stratus
|
|
510
|
+
" /stratus setup - Interactive setup wizard (Formation pool)\n" +
|
|
511
|
+
" /stratus setup <stratus_sk_...> - Non-interactive setup with API key\n" +
|
|
512
|
+
" /stratus verify - Verify configuration & connectivity\n" +
|
|
513
|
+
" /stratus models - List all available models (live from API)\n\n" +
|
|
514
|
+
"Agent/programmatic setup:\n" +
|
|
515
|
+
" /stratus setup stratus_sk_your_key_here\n" +
|
|
516
|
+
" # Or: set STRATUS_API_KEY env var → gateway auto-configures on start\n\n" +
|
|
376
517
|
"Get your API key: https://stratus.run\n" +
|
|
377
518
|
"Docs: https://stratus.run/docs\n" +
|
|
378
519
|
"Issues: https://github.com/formthefog/openclaw-stratus-x1-plugin/issues",
|
package/openclaw.plugin.json
CHANGED
|
@@ -12,46 +12,70 @@
|
|
|
12
12
|
"default": true,
|
|
13
13
|
"description": "Enable Stratus plugin"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
16
|
-
"type": "string",
|
|
17
|
-
"description": "Stratus API key (or use STRATUS_API_KEY env var)"
|
|
18
|
-
},
|
|
19
|
-
"baseUrl": {
|
|
20
|
-
"type": "string",
|
|
21
|
-
"default": "https://api.stratus.run",
|
|
22
|
-
"description": "Stratus API base URL"
|
|
23
|
-
},
|
|
24
|
-
"provider": {
|
|
15
|
+
"config": {
|
|
25
16
|
"type": "object",
|
|
17
|
+
"description": "Stratus plugin configuration",
|
|
26
18
|
"properties": {
|
|
27
|
-
"
|
|
28
|
-
"type": "
|
|
29
|
-
"
|
|
19
|
+
"apiKey": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Stratus API key (optional — Formation pool used as fallback)"
|
|
30
22
|
},
|
|
31
|
-
"
|
|
23
|
+
"baseUrl": {
|
|
32
24
|
"type": "string",
|
|
33
|
-
"default": "stratus
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"tools": {
|
|
38
|
-
"type": "object",
|
|
39
|
-
"properties": {
|
|
40
|
-
"embeddings": {
|
|
25
|
+
"default": "https://api.stratus.run",
|
|
26
|
+
"description": "Stratus API base URL"
|
|
27
|
+
},
|
|
28
|
+
"inlineKeys": {
|
|
41
29
|
"type": "object",
|
|
30
|
+
"description": "Optional BYOK keys passed per-request to bypass Formation pool",
|
|
42
31
|
"properties": {
|
|
43
|
-
"
|
|
44
|
-
"type": "
|
|
45
|
-
"
|
|
32
|
+
"openai_key": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "OpenAI API key for BYOK passthrough"
|
|
35
|
+
},
|
|
36
|
+
"anthropic_key": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Anthropic API key for BYOK passthrough"
|
|
39
|
+
},
|
|
40
|
+
"gemini_key": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Google Gemini API key for BYOK passthrough (also sent as X-Google-Key header)"
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
},
|
|
49
|
-
"
|
|
46
|
+
"provider": {
|
|
50
47
|
"type": "object",
|
|
51
48
|
"properties": {
|
|
52
49
|
"enabled": {
|
|
53
50
|
"type": "boolean",
|
|
54
51
|
"default": true
|
|
52
|
+
},
|
|
53
|
+
"defaultModel": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"default": "stratus-x1ac-base-claude-sonnet-4-5"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"tools": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"embeddings": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"properties": {
|
|
65
|
+
"enabled": {
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"default": true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"rollout": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"properties": {
|
|
74
|
+
"enabled": {
|
|
75
|
+
"type": "boolean",
|
|
76
|
+
"default": true
|
|
77
|
+
}
|
|
78
|
+
}
|
|
55
79
|
}
|
|
56
80
|
}
|
|
57
81
|
}
|