simmer-automaton 0.1.4 → 0.1.6

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/dist/api.d.ts CHANGED
@@ -19,10 +19,7 @@ export interface Skill {
19
19
  category: string;
20
20
  tags: string[];
21
21
  difficulty: string;
22
- install: string;
23
- clawhub_url: string;
24
- requires: string[];
25
- best_when: string | null;
22
+ enabled: boolean;
26
23
  }
27
24
  export declare class SimmerApi {
28
25
  private baseUrl;
@@ -37,4 +34,10 @@ export declare class SimmerApi {
37
34
  skills: Skill[];
38
35
  total: number;
39
36
  }>;
37
+ getAutomatonSkills(): Promise<{
38
+ skills: Skill[];
39
+ total: number;
40
+ }>;
41
+ disableSkill(slug: string): Promise<unknown>;
42
+ enableSkill(slug: string): Promise<unknown>;
40
43
  }
package/dist/api.js CHANGED
@@ -42,4 +42,13 @@ export class SimmerApi {
42
42
  async getSkills() {
43
43
  return this.request("/api/sdk/skills");
44
44
  }
45
+ async getAutomatonSkills() {
46
+ return this.request("/api/sdk/automaton/skills");
47
+ }
48
+ async disableSkill(slug) {
49
+ return this.request(`/api/sdk/automaton/skills/${encodeURIComponent(slug)}/disable`, { method: "POST" });
50
+ }
51
+ async enableSkill(slug) {
52
+ return this.request(`/api/sdk/automaton/skills/${encodeURIComponent(slug)}/enable`, { method: "POST" });
53
+ }
45
54
  }
package/dist/index.js CHANGED
@@ -53,8 +53,16 @@ function loadConfig(pluginConfig) {
53
53
  async function refreshState(logger) {
54
54
  try {
55
55
  cachedState = await api.getAutomatonState();
56
- const skillsRes = await api.getSkills();
57
- cachedSkills = skillsRes.skills;
56
+ // Use automaton skills endpoint (respects per-user enable/disable prefs)
57
+ try {
58
+ const res = await api.getAutomatonSkills();
59
+ cachedSkills = res.skills;
60
+ }
61
+ catch {
62
+ // Fall back to generic skills endpoint if automaton endpoint not available
63
+ const res = await api.getSkills();
64
+ cachedSkills = res.skills;
65
+ }
58
66
  if (cachedState.initialized) {
59
67
  // Compute tier (totalPnl = 0 for now, will be enriched when P&L tracking is added)
60
68
  currentTier = computeTier(cachedState, 0);
@@ -64,19 +72,19 @@ async function refreshState(logger) {
64
72
  logger.error(`[simmer] Failed to refresh state: ${e}`);
65
73
  }
66
74
  }
67
- function currencyLabel() {
68
- return config.venue === "simmer" ? "$SIM" : "$";
75
+ function fmtCurrency(amount) {
76
+ const val = amount.toFixed(2);
77
+ return config.venue === "simmer" ? `${val} $SIM` : `$${val}`;
69
78
  }
70
79
  function buildPromptContext() {
71
80
  if (!cachedState || !cachedState.initialized) {
72
81
  return "[Simmer Automaton] Not initialized. Run: POST /api/sdk/automaton/init";
73
82
  }
74
- const c = currencyLabel();
75
83
  const lines = [
76
84
  "## Simmer Automaton — Survival Context (read-only)",
77
85
  "",
78
86
  `**Tier:** ${currentTier} | **Venue:** ${config.venue}`,
79
- `**Budget:** ${c}${cachedState.budget_usd.toFixed(2)} | **Spent:** ${c}${cachedState.spent_usd.toFixed(2)} | **Remaining:** ${c}${cachedState.remaining_usd.toFixed(2)}`,
87
+ `**Budget:** ${fmtCurrency(cachedState.budget_usd)} | **Spent:** ${fmtCurrency(cachedState.spent_usd)} | **Remaining:** ${fmtCurrency(cachedState.remaining_usd)}`,
80
88
  `**Halted:** ${cachedState.halted ? "YES — all trades blocked" : "no"}`,
81
89
  `**Horizon:** ${cachedState.horizon_days} days`,
82
90
  "",
@@ -114,13 +122,12 @@ function formatStatus() {
114
122
  if (!cachedState || !cachedState.initialized) {
115
123
  return "Automaton not initialized. Use POST /api/sdk/automaton/init to start.";
116
124
  }
117
- const c = currencyLabel();
118
125
  const lines = [
119
126
  `Tier: ${currentTier}`,
120
127
  `Venue: ${config.venue}`,
121
- `Budget: ${c}${cachedState.budget_usd.toFixed(2)}`,
122
- `Spent: ${c}${cachedState.spent_usd.toFixed(2)}`,
123
- `Remaining: ${c}${cachedState.remaining_usd.toFixed(2)}`,
128
+ `Budget: ${fmtCurrency(cachedState.budget_usd)}`,
129
+ `Spent: ${fmtCurrency(cachedState.spent_usd)}`,
130
+ `Remaining: ${fmtCurrency(cachedState.remaining_usd)}`,
124
131
  `Halted: ${cachedState.halted ? "YES" : "no"}`,
125
132
  `Horizon: ${cachedState.horizon_days} days`,
126
133
  `Cycles: ${cycleCount}`,
@@ -215,11 +222,42 @@ export default function register(pluginApi) {
215
222
  if (cachedSkills.length === 0) {
216
223
  return { text: "No skills in registry." };
217
224
  }
218
- const lines = cachedSkills.map((s) => `- ${s.name} (${s.id}) — ${s.category}, ${s.difficulty}`);
225
+ const lines = cachedSkills.map((s) => {
226
+ const status = s.enabled === false ? " [DISABLED]" : "";
227
+ return `- ${s.name} (${s.id}) — ${s.category}, ${s.difficulty}${status}`;
228
+ });
219
229
  return { text: `Skills (${cachedSkills.length}):\n${lines.join("\n")}` };
220
230
  }
231
+ if (subcommand === "disable") {
232
+ const slug = ctx.args?.trim().split(/\s+/)[1];
233
+ if (!slug) {
234
+ return { text: "Usage: /simmer disable <skill-slug>" };
235
+ }
236
+ try {
237
+ await api.disableSkill(slug);
238
+ await refreshState(logger);
239
+ return { text: `Disabled skill: ${slug}. It won't be selected by the automaton.` };
240
+ }
241
+ catch (e) {
242
+ return { text: `Failed to disable: ${e}` };
243
+ }
244
+ }
245
+ if (subcommand === "enable") {
246
+ const slug = ctx.args?.trim().split(/\s+/)[1];
247
+ if (!slug) {
248
+ return { text: "Usage: /simmer enable <skill-slug>" };
249
+ }
250
+ try {
251
+ await api.enableSkill(slug);
252
+ await refreshState(logger);
253
+ return { text: `Enabled skill: ${slug}. It will be included in the automaton's skill pool.` };
254
+ }
255
+ catch (e) {
256
+ return { text: `Failed to enable: ${e}` };
257
+ }
258
+ }
221
259
  return {
222
- text: "Usage: /simmer [status|halt|resume|skills]",
260
+ text: "Usage: /simmer [status|halt|resume|skills|disable <slug>|enable <slug>]",
223
261
  };
224
262
  },
225
263
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simmer-automaton",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Simmer Automaton plugin for OpenClaw — adaptive trading skill orchestration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/api.ts CHANGED
@@ -21,10 +21,7 @@ export interface Skill {
21
21
  category: string;
22
22
  tags: string[];
23
23
  difficulty: string;
24
- install: string;
25
- clawhub_url: string;
26
- requires: string[];
27
- best_when: string | null;
24
+ enabled: boolean;
28
25
  }
29
26
 
30
27
  export class SimmerApi {
@@ -74,4 +71,16 @@ export class SimmerApi {
74
71
  async getSkills(): Promise<{ skills: Skill[]; total: number }> {
75
72
  return this.request("/api/sdk/skills");
76
73
  }
74
+
75
+ async getAutomatonSkills(): Promise<{ skills: Skill[]; total: number }> {
76
+ return this.request("/api/sdk/automaton/skills");
77
+ }
78
+
79
+ async disableSkill(slug: string) {
80
+ return this.request(`/api/sdk/automaton/skills/${encodeURIComponent(slug)}/disable`, { method: "POST" });
81
+ }
82
+
83
+ async enableSkill(slug: string) {
84
+ return this.request(`/api/sdk/automaton/skills/${encodeURIComponent(slug)}/enable`, { method: "POST" });
85
+ }
77
86
  }
package/src/index.ts CHANGED
@@ -68,8 +68,15 @@ function loadConfig(pluginConfig?: Record<string, unknown>) {
68
68
  async function refreshState(logger: { info: (m: string) => void; error: (m: string) => void }) {
69
69
  try {
70
70
  cachedState = await api.getAutomatonState();
71
- const skillsRes = await api.getSkills();
72
- cachedSkills = skillsRes.skills;
71
+ // Use automaton skills endpoint (respects per-user enable/disable prefs)
72
+ try {
73
+ const res = await api.getAutomatonSkills();
74
+ cachedSkills = res.skills;
75
+ } catch {
76
+ // Fall back to generic skills endpoint if automaton endpoint not available
77
+ const res = await api.getSkills();
78
+ cachedSkills = res.skills;
79
+ }
73
80
 
74
81
  if (cachedState.initialized) {
75
82
  // Compute tier (totalPnl = 0 for now, will be enriched when P&L tracking is added)
@@ -80,8 +87,9 @@ async function refreshState(logger: { info: (m: string) => void; error: (m: stri
80
87
  }
81
88
  }
82
89
 
83
- function currencyLabel(): string {
84
- return config.venue === "simmer" ? "$SIM" : "$";
90
+ function fmtCurrency(amount: number): string {
91
+ const val = amount.toFixed(2);
92
+ return config.venue === "simmer" ? `${val} $SIM` : `$${val}`;
85
93
  }
86
94
 
87
95
  function buildPromptContext(): string {
@@ -89,12 +97,11 @@ function buildPromptContext(): string {
89
97
  return "[Simmer Automaton] Not initialized. Run: POST /api/sdk/automaton/init";
90
98
  }
91
99
 
92
- const c = currencyLabel();
93
100
  const lines = [
94
101
  "## Simmer Automaton — Survival Context (read-only)",
95
102
  "",
96
103
  `**Tier:** ${currentTier} | **Venue:** ${config.venue}`,
97
- `**Budget:** ${c}${cachedState.budget_usd.toFixed(2)} | **Spent:** ${c}${cachedState.spent_usd.toFixed(2)} | **Remaining:** ${c}${cachedState.remaining_usd.toFixed(2)}`,
104
+ `**Budget:** ${fmtCurrency(cachedState.budget_usd)} | **Spent:** ${fmtCurrency(cachedState.spent_usd)} | **Remaining:** ${fmtCurrency(cachedState.remaining_usd)}`,
98
105
  `**Halted:** ${cachedState.halted ? "YES — all trades blocked" : "no"}`,
99
106
  `**Horizon:** ${cachedState.horizon_days} days`,
100
107
  "",
@@ -137,13 +144,12 @@ function formatStatus(): string {
137
144
  return "Automaton not initialized. Use POST /api/sdk/automaton/init to start.";
138
145
  }
139
146
 
140
- const c = currencyLabel();
141
147
  const lines = [
142
148
  `Tier: ${currentTier}`,
143
149
  `Venue: ${config.venue}`,
144
- `Budget: ${c}${cachedState.budget_usd.toFixed(2)}`,
145
- `Spent: ${c}${cachedState.spent_usd.toFixed(2)}`,
146
- `Remaining: ${c}${cachedState.remaining_usd.toFixed(2)}`,
150
+ `Budget: ${fmtCurrency(cachedState.budget_usd)}`,
151
+ `Spent: ${fmtCurrency(cachedState.spent_usd)}`,
152
+ `Remaining: ${fmtCurrency(cachedState.remaining_usd)}`,
147
153
  `Halted: ${cachedState.halted ? "YES" : "no"}`,
148
154
  `Horizon: ${cachedState.horizon_days} days`,
149
155
  `Cycles: ${cycleCount}`,
@@ -258,13 +264,44 @@ export default function register(pluginApi: PluginApi) {
258
264
  return { text: "No skills in registry." };
259
265
  }
260
266
  const lines = cachedSkills.map(
261
- (s) => `- ${s.name} (${s.id}) — ${s.category}, ${s.difficulty}`,
267
+ (s) => {
268
+ const status = (s as any).enabled === false ? " [DISABLED]" : "";
269
+ return `- ${s.name} (${s.id}) — ${s.category}, ${s.difficulty}${status}`;
270
+ },
262
271
  );
263
272
  return { text: `Skills (${cachedSkills.length}):\n${lines.join("\n")}` };
264
273
  }
265
274
 
275
+ if (subcommand === "disable") {
276
+ const slug = ctx.args?.trim().split(/\s+/)[1];
277
+ if (!slug) {
278
+ return { text: "Usage: /simmer disable <skill-slug>" };
279
+ }
280
+ try {
281
+ await api.disableSkill(slug);
282
+ await refreshState(logger);
283
+ return { text: `Disabled skill: ${slug}. It won't be selected by the automaton.` };
284
+ } catch (e) {
285
+ return { text: `Failed to disable: ${e}` };
286
+ }
287
+ }
288
+
289
+ if (subcommand === "enable") {
290
+ const slug = ctx.args?.trim().split(/\s+/)[1];
291
+ if (!slug) {
292
+ return { text: "Usage: /simmer enable <skill-slug>" };
293
+ }
294
+ try {
295
+ await api.enableSkill(slug);
296
+ await refreshState(logger);
297
+ return { text: `Enabled skill: ${slug}. It will be included in the automaton's skill pool.` };
298
+ } catch (e) {
299
+ return { text: `Failed to enable: ${e}` };
300
+ }
301
+ }
302
+
266
303
  return {
267
- text: "Usage: /simmer [status|halt|resume|skills]",
304
+ text: "Usage: /simmer [status|halt|resume|skills|disable <slug>|enable <slug>]",
268
305
  };
269
306
  },
270
307
  });