@reefclaw/openclaw-plugin 0.1.0 → 0.1.1
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/config/plugin-config-io.d.ts +13 -0
- package/config/plugin-config-io.js +27 -0
- package/config/user-data-stream-config.d.ts +4 -0
- package/config/user-data-stream-config.js +17 -2
- package/index.js +15 -4
- package/package.json +11 -3
- package/skills/reefclaw/SKILL.md +2 -2
- package/strategy/builtin-strategies.js +7 -3
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { TradingMode, ExchangeConfig } from '../types.js';
|
|
2
|
+
/** The connection the AGENT saves during onboarding lives in OpenClaw's own
|
|
3
|
+
* config (`skills.entries.reefclaw.config` in ~/.openclaw/openclaw.json) —
|
|
4
|
+
* the connector reads it there, and since the chat-install flow never writes
|
|
5
|
+
* ~/.reefclaw/plugin-config.json, the PLUGIN must fall back to it too or
|
|
6
|
+
* every fresh install has working dashboards but token-starved intel tools,
|
|
7
|
+
* journaling ingest, and config polling (hit live on the first real user,
|
|
8
|
+
* 2026-07-06). Nested `.config.*` is the schema-valid shape; legacy flat
|
|
9
|
+
* fields are read as a fallback. Fail-soft: {} on any problem. */
|
|
10
|
+
export declare function readOpenClawConnection(): {
|
|
11
|
+
token?: string;
|
|
12
|
+
userId?: string;
|
|
13
|
+
relayUrl?: string;
|
|
14
|
+
};
|
|
2
15
|
/** Full on-disk schema. Unknown keys are preserved on round-trip. */
|
|
3
16
|
export interface PluginConfigFile {
|
|
4
17
|
connectionToken?: string;
|
|
@@ -8,6 +8,33 @@
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
|
+
/** The connection the AGENT saves during onboarding lives in OpenClaw's own
|
|
12
|
+
* config (`skills.entries.reefclaw.config` in ~/.openclaw/openclaw.json) —
|
|
13
|
+
* the connector reads it there, and since the chat-install flow never writes
|
|
14
|
+
* ~/.reefclaw/plugin-config.json, the PLUGIN must fall back to it too or
|
|
15
|
+
* every fresh install has working dashboards but token-starved intel tools,
|
|
16
|
+
* journaling ingest, and config polling (hit live on the first real user,
|
|
17
|
+
* 2026-07-06). Nested `.config.*` is the schema-valid shape; legacy flat
|
|
18
|
+
* fields are read as a fallback. Fail-soft: {} on any problem. */
|
|
19
|
+
export function readOpenClawConnection() {
|
|
20
|
+
try {
|
|
21
|
+
const path = process.env.OPENCLAW_CONFIG_PATH ?? join(homedir(), '.openclaw', 'openclaw.json');
|
|
22
|
+
if (!existsSync(path))
|
|
23
|
+
return {};
|
|
24
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
25
|
+
const entry = parsed?.skills?.entries?.reefclaw;
|
|
26
|
+
if (!entry)
|
|
27
|
+
return {};
|
|
28
|
+
return {
|
|
29
|
+
token: entry.config?.token ?? entry.token,
|
|
30
|
+
userId: entry.config?.userId ?? entry.userId,
|
|
31
|
+
relayUrl: entry.config?.relayUrl ?? entry.relayUrl,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
11
38
|
export function defaultConfigPath() {
|
|
12
39
|
return join(homedir(), '.reefclaw', 'plugin-config.json');
|
|
13
40
|
}
|
|
@@ -64,6 +64,10 @@ export declare function getUserDataStreamIngestBaseUrl(config?: PluginConfigFile
|
|
|
64
64
|
*
|
|
65
65
|
* Both ingest paths (position-decisions journal + WS audit-trail) use this. */
|
|
66
66
|
export declare function resolveIngestToken(config?: PluginConfigFile): string;
|
|
67
|
+
/** Resolve the trader's ReefClaw userId: env (legacy prod deployments) →
|
|
68
|
+
* the connection the agent saved in OpenClaw's config. Ingest paths need it
|
|
69
|
+
* alongside the token; fresh installs only ever have the latter source. */
|
|
70
|
+
export declare function resolveReefclawUserId(): string;
|
|
67
71
|
/** Is the WS authoritative for reads (observe or enforce)? In shadow it's
|
|
68
72
|
* only observed — REST remains the source of truth for getPositions etc. */
|
|
69
73
|
export declare function userDataStreamAuthoritative(mode: UserDataStreamMode): boolean;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// Transitions must progress forward only (no skipping). Deploy script enforces.
|
|
21
21
|
// Reverse transitions to any earlier stage (including 'shadow' and 'off') are
|
|
22
22
|
// always permitted as a safety-net rollback.
|
|
23
|
-
import { readPluginConfig } from './plugin-config-io.js';
|
|
23
|
+
import { readPluginConfig, readOpenClawConnection } from './plugin-config-io.js';
|
|
24
24
|
const VALID = new Set(['off', 'shadow', 'observe', 'enforce']);
|
|
25
25
|
/** Allowed forward transitions. Reverse transitions are always permitted so
|
|
26
26
|
* the operator can fall back to REST if WS misbehaves.
|
|
@@ -138,7 +138,22 @@ export function resolveIngestToken(config) {
|
|
|
138
138
|
const fromConfig = config?.connectionToken;
|
|
139
139
|
if (typeof fromConfig === 'string' && fromConfig.trim())
|
|
140
140
|
return fromConfig.trim();
|
|
141
|
-
|
|
141
|
+
const fromEnv = process.env.WEBAPP_INGEST_TOKEN ?? '';
|
|
142
|
+
if (fromEnv.trim())
|
|
143
|
+
return fromEnv.trim();
|
|
144
|
+
// Chat/npx onboarding stores the token only where the AGENT saves it —
|
|
145
|
+
// OpenClaw's own config. Without this fallback every fresh install has a
|
|
146
|
+
// token-starved plugin (intel tools, ingest, config poller all dead).
|
|
147
|
+
return readOpenClawConnection().token?.trim() ?? '';
|
|
148
|
+
}
|
|
149
|
+
/** Resolve the trader's ReefClaw userId: env (legacy prod deployments) →
|
|
150
|
+
* the connection the agent saved in OpenClaw's config. Ingest paths need it
|
|
151
|
+
* alongside the token; fresh installs only ever have the latter source. */
|
|
152
|
+
export function resolveReefclawUserId() {
|
|
153
|
+
const fromEnv = process.env.REEFCLAW_USER_ID ?? '';
|
|
154
|
+
if (fromEnv.trim())
|
|
155
|
+
return fromEnv.trim();
|
|
156
|
+
return readOpenClawConnection().userId?.trim() ?? '';
|
|
142
157
|
}
|
|
143
158
|
/** Is the WS authoritative for reads (observe or enforce)? In shadow it's
|
|
144
159
|
* only observed — REST remains the source of truth for getPositions etc. */
|
package/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { DEFAULT_CONFIG } from './types.js';
|
|
|
20
20
|
import { PaperAdapter } from './paper-adapter.js';
|
|
21
21
|
import { LiveAdapter } from './live/live-adapter.js';
|
|
22
22
|
import { loadBracketMode } from './config/brackets-config.js';
|
|
23
|
-
import { loadUserDataStreamMode, loadUserDataStreamTunables, loadUserDataStreamDbWrite, getUserDataStreamIngestBaseUrl, resolveIngestToken, } from './config/user-data-stream-config.js';
|
|
23
|
+
import { loadUserDataStreamMode, loadUserDataStreamTunables, loadUserDataStreamDbWrite, getUserDataStreamIngestBaseUrl, resolveIngestToken, resolveReefclawUserId, } from './config/user-data-stream-config.js';
|
|
24
24
|
import { TradeStoreClient } from './ingest/trade-store-client.js';
|
|
25
25
|
import { ProposalManager } from './live/proposal-manager.js';
|
|
26
26
|
import { ProposalDecisionListener } from './live/proposal-decision-listener.js';
|
|
@@ -39,7 +39,7 @@ import { proposeLearningTool } from './tools/propose-learning.js';
|
|
|
39
39
|
import { queryReviewOutcomesTool } from './tools/query-review-outcomes.js';
|
|
40
40
|
import { loadPositionReviewMode } from './config/position-review-config.js';
|
|
41
41
|
import { installSignalHandlers } from './lifecycle/install-signal-handlers.js';
|
|
42
|
-
import { readPluginConfig } from './config/plugin-config-io.js';
|
|
42
|
+
import { readPluginConfig, readOpenClawConnection } from './config/plugin-config-io.js';
|
|
43
43
|
import { startConnectorSupervisor, hasBundledBridge } from './connector-supervisor.js';
|
|
44
44
|
import { ToolGate } from './config/tool-gate.js';
|
|
45
45
|
import { gateStore } from './config/gate-store.js';
|
|
@@ -945,6 +945,17 @@ const paperTradingPlugin = {
|
|
|
945
945
|
catch (err) {
|
|
946
946
|
logger.warn(TAG, `Could not read ReefClaw config: ${formatError(err)}`);
|
|
947
947
|
}
|
|
948
|
+
if (!connectionToken) {
|
|
949
|
+
// Chat/npx onboarding saves the connection ONLY in OpenClaw's config
|
|
950
|
+
// (skills.entries.reefclaw.config) — the agent writes it there and the
|
|
951
|
+
// connector reads it there. Fall back so a fresh install's intel tools,
|
|
952
|
+
// ingest, and config poller aren't token-starved (first-user bug).
|
|
953
|
+
const conn = readOpenClawConnection();
|
|
954
|
+
if (conn.token) {
|
|
955
|
+
connectionToken = conn.token;
|
|
956
|
+
logger.info(TAG, 'Connection token loaded from OpenClaw config (skills.entries.reefclaw)');
|
|
957
|
+
}
|
|
958
|
+
}
|
|
948
959
|
if (connectionToken) {
|
|
949
960
|
logger.info(TAG, `Connection token loaded (prefix: ${connectionToken.slice(0, 7)}...)`);
|
|
950
961
|
}
|
|
@@ -1020,7 +1031,7 @@ const paperTradingPlugin = {
|
|
|
1020
1031
|
// Ingest token: plugin-config.json `connectionToken` preferred, legacy
|
|
1021
1032
|
// WEBAPP_INGEST_TOKEN env as fallback — see resolveIngestToken.
|
|
1022
1033
|
const ingestToken = resolveIngestToken(cfgForIngest);
|
|
1023
|
-
const reefclawUserId =
|
|
1034
|
+
const reefclawUserId = resolveReefclawUserId();
|
|
1024
1035
|
if (ingestToken && reefclawUserId) {
|
|
1025
1036
|
const ingestBaseUrl = getUserDataStreamIngestBaseUrl(cfgForIngest);
|
|
1026
1037
|
positionDecisionsClient = new PositionDecisionsClient({
|
|
@@ -1124,7 +1135,7 @@ const paperTradingPlugin = {
|
|
|
1124
1135
|
cfgForIngest = undefined;
|
|
1125
1136
|
}
|
|
1126
1137
|
const ingestToken = resolveIngestToken(cfgForIngest);
|
|
1127
|
-
const reefclawUserId =
|
|
1138
|
+
const reefclawUserId = resolveReefclawUserId();
|
|
1128
1139
|
if (userDataStreamMode !== 'off' &&
|
|
1129
1140
|
dbWriteMode === 'on' &&
|
|
1130
1141
|
ingestToken &&
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reefclaw/openclaw-plugin",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "ReefClaw trading plugin for OpenClaw
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "ReefClaw trading plugin for OpenClaw \u2014 paper trading with real Binance market data, plus the ReefClaw dashboard connector (supervised by OpenClaw, no service manager needed). Install: /plugins install clawhub:@reefclaw/openclaw-plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"openclaw": {
|
|
8
|
-
"extensions": [
|
|
8
|
+
"extensions": [
|
|
9
|
+
"./index.js"
|
|
10
|
+
],
|
|
11
|
+
"compat": {
|
|
12
|
+
"pluginApi": ">=2026.6.0"
|
|
13
|
+
},
|
|
14
|
+
"build": {
|
|
15
|
+
"openclawVersion": "2026.6.11"
|
|
16
|
+
}
|
|
9
17
|
},
|
|
10
18
|
"files": [
|
|
11
19
|
"**/*"
|
package/skills/reefclaw/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reefclaw
|
|
3
|
-
version: 0.0.
|
|
3
|
+
version: 0.0.5
|
|
4
4
|
description: ReefClaw trading control room — bootstrap (connects your agent; full trading instructions arrive automatically after first connect)
|
|
5
5
|
author: ReefClaw
|
|
6
6
|
homepage: https://reefclaw.com
|
|
@@ -72,7 +72,7 @@ OpenClaw can install the ReefClaw plugin through its own owner chat command —
|
|
|
72
72
|
|
|
73
73
|
1. In `~/.openclaw/openclaw.json` set `commands.plugins: true` and add the user's **sender id** (visible in this session) to `commands.ownerAllowFrom`.
|
|
74
74
|
2. Tell the user to type `/restart` in this chat.
|
|
75
|
-
3. Then the user types: `/plugins install
|
|
75
|
+
3. Then the user types: `/plugins install clawhub:@reefclaw/openclaw-plugin` — the install restarts OpenClaw automatically; the bundled connector starts by itself and the dashboard flips to Connected.
|
|
76
76
|
|
|
77
77
|
Only fall back to `npx @reefclaw/connect` (below) when the chat install is unavailable.
|
|
78
78
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// LEGACY strategy configs — used ONLY as fixtures by the local-evaluator
|
|
2
|
+
// tests (strategy/__tests__/evaluator.test.ts). NOT seeded anywhere and NOT
|
|
3
|
+
// part of the product catalog: the shipped catalog lives in
|
|
4
|
+
// intelligence/src/signals/strategy-store.ts (exactly two template
|
|
5
|
+
// strategies since the 2026-07-06 trim — see CLAUDE.md), and the plugin's
|
|
6
|
+
// local evaluator (signals.evaluator, default 'central') receives its
|
|
7
|
+
// strategy set from the intel service, not from this file.
|
|
4
8
|
export const BUILTIN_STRATEGIES = [
|
|
5
9
|
{
|
|
6
10
|
name: 'trend_continuation',
|