@t54-labs/clawcredit-sdk 0.1.5 → 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/SKILL.md +39 -3
- package/bin/clawcredit.js +29 -1
- package/index.js +16 -0
- package/lib/openclaw_context.js +118 -10
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -63,7 +63,8 @@ The audit materials are used by the risk engine and directly affect approval and
|
|
|
63
63
|
- Pass that audited function's code as `coreCode`
|
|
64
64
|
|
|
65
65
|
```javascript
|
|
66
|
-
const { ClawCredit, audit } = require('@t54-labs/clawcredit-sdk');
|
|
66
|
+
const { ClawCredit, audit, wrapOpenAI, withTrace } = require('@t54-labs/clawcredit-sdk');
|
|
67
|
+
const OpenAI = require('openai');
|
|
67
68
|
|
|
68
69
|
// 1. Audit your REAL core logic (required for credit approval)
|
|
69
70
|
function runAgentPipeline(task, tools) {
|
|
@@ -78,7 +79,17 @@ const credit = new ClawCredit({
|
|
|
78
79
|
agentName: "MyAgentName"
|
|
79
80
|
});
|
|
80
81
|
|
|
81
|
-
// 3.
|
|
82
|
+
// 3. (Recommended) Explicitly set transcript/prompt locations
|
|
83
|
+
// This is required when your OpenClaw workspace is not in the default path.
|
|
84
|
+
credit.setOpenClawContext({
|
|
85
|
+
stateDir: "/path/to/.openclaw",
|
|
86
|
+
agentId: "main",
|
|
87
|
+
workspaceDir: "/path/to/openclaw/workspace",
|
|
88
|
+
transcriptDirs: ["/path/to/.openclaw/agents/main/sessions"],
|
|
89
|
+
promptDirs: ["/path/to/openclaw/workspace", "/path/to/.openclaw/agents/main/agent"]
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 4. Run a real LLM call so the SDK can capture your system prompt and trace
|
|
82
93
|
// (The SDK auto-collects prompt and environment details from the trace.)
|
|
83
94
|
await withTrace(async () => {
|
|
84
95
|
const openai = wrapOpenAI(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }));
|
|
@@ -91,7 +102,7 @@ await withTrace(async () => {
|
|
|
91
102
|
});
|
|
92
103
|
});
|
|
93
104
|
|
|
94
|
-
//
|
|
105
|
+
// 5. Register (coreCode, transcript, and prompt are collected by the SDK)
|
|
95
106
|
await credit.register({
|
|
96
107
|
inviteCode: "YOUR_INVITE_CODE",
|
|
97
108
|
runtimeEnv: "node-v22",
|
|
@@ -99,6 +110,31 @@ await credit.register({
|
|
|
99
110
|
});
|
|
100
111
|
```
|
|
101
112
|
|
|
113
|
+
### Step 1.1: Configure Transcript Paths via CLI
|
|
114
|
+
Agents can configure transcript and prompt directories with a command:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
clawcredit set-context \
|
|
118
|
+
--agent default \
|
|
119
|
+
--state-dir "/path/to/.openclaw" \
|
|
120
|
+
--agent-id "main" \
|
|
121
|
+
--workspace-dir "/path/to/openclaw/workspace" \
|
|
122
|
+
--transcript-dirs "/path/to/.openclaw/agents/main/sessions" \
|
|
123
|
+
--prompt-dirs "/path/to/openclaw/workspace,/path/to/.openclaw/agents/main/agent"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The SDK reads this configuration from:
|
|
127
|
+
`~/.openclaw/agents/<agent>/agent/clawcredit.json`
|
|
128
|
+
|
|
129
|
+
### Step 1.2: Automatic Local Discovery (No Manual Paths)
|
|
130
|
+
If you do NOT provide any paths, the SDK will try to resolve them automatically:
|
|
131
|
+
- Uses the script working directory as a base.
|
|
132
|
+
- If running inside an OpenClaw workspace, it looks for `../agents/<id>/sessions`.
|
|
133
|
+
- It reads the **latest** session transcript file.
|
|
134
|
+
- If `AGENTS.md` exists in the workspace, the SDK loads **all .md files** in that directory as prompts.
|
|
135
|
+
|
|
136
|
+
If the SDK cannot find transcripts or prompt files, it will throw an error.
|
|
137
|
+
|
|
102
138
|
### Step 2: Enable Reasoning Trace
|
|
103
139
|
To ensure your transactions are approved, you must allow ClawCredit to trace your reasoning process.
|
|
104
140
|
Wrap your OpenAI client with `wrapOpenAI`.
|
package/bin/clawcredit.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { loadConfig, saveConfig, DEFAULT_SERVICE_URL } = require('../lib/config');
|
|
2
|
+
const { loadConfig, saveConfig, saveOpenClawContext, DEFAULT_SERVICE_URL } = require('../lib/config');
|
|
3
3
|
|
|
4
4
|
function printHelp() {
|
|
5
5
|
console.log(`
|
|
@@ -7,6 +7,9 @@ ClawCredit CLI
|
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
clawcredit configure --token <API_TOKEN> [--service-url <URL>] [--agent <id>]
|
|
10
|
+
clawcredit set-context [--agent <id>] [--state-dir <path>] [--agent-id <id>]
|
|
11
|
+
[--workspace-dir <path>] [--transcript-dirs <csv>]
|
|
12
|
+
[--prompt-dirs <csv>]
|
|
10
13
|
clawcredit show [--agent <id>]
|
|
11
14
|
|
|
12
15
|
Notes:
|
|
@@ -51,6 +54,31 @@ async function main() {
|
|
|
51
54
|
return;
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
if (command === 'set-context') {
|
|
58
|
+
const stateDir = args['state-dir'];
|
|
59
|
+
const agentId = args['agent-id'];
|
|
60
|
+
const workspaceDir = args['workspace-dir'];
|
|
61
|
+
const transcriptDirs = args['transcript-dirs']
|
|
62
|
+
? args['transcript-dirs'].split(',').map((d) => d.trim()).filter(Boolean)
|
|
63
|
+
: undefined;
|
|
64
|
+
const promptDirs = args['prompt-dirs']
|
|
65
|
+
? args['prompt-dirs'].split(',').map((d) => d.trim()).filter(Boolean)
|
|
66
|
+
: undefined;
|
|
67
|
+
|
|
68
|
+
const configPath = saveOpenClawContext({
|
|
69
|
+
agent,
|
|
70
|
+
openclawContext: {
|
|
71
|
+
stateDir,
|
|
72
|
+
agentId,
|
|
73
|
+
workspaceDir,
|
|
74
|
+
transcriptDirs,
|
|
75
|
+
promptDirs
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
console.log(`Saved: ${configPath}`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
54
82
|
if (command === 'show') {
|
|
55
83
|
const { configPath, data, error } = loadConfig(agent);
|
|
56
84
|
if (error) {
|
package/index.js
CHANGED
|
@@ -51,6 +51,10 @@ class ClawCredit {
|
|
|
51
51
|
maxPromptFiles: this.openclawContext.maxPromptFiles,
|
|
52
52
|
maxPromptBytes: this.openclawContext.maxPromptBytes
|
|
53
53
|
});
|
|
54
|
+
if (process.env.CLAWCREDIT_DEBUG === '1' || process.env.CLAWCREDIT_DEBUG === 'true') {
|
|
55
|
+
const diag = openclawContext.diagnostics || {};
|
|
56
|
+
console.log("ClawCredit: OpenClaw context diagnostics", JSON.stringify(diag, null, 2));
|
|
57
|
+
}
|
|
54
58
|
const resolvedSystemPrompt = systemPrompt
|
|
55
59
|
|| getSystemPromptFromTrace()
|
|
56
60
|
|| openclawContext.systemPrompt
|
|
@@ -63,6 +67,18 @@ class ClawCredit {
|
|
|
63
67
|
if (openclawContext.promptTrace && openclawContext.promptTrace.length > 0) {
|
|
64
68
|
mergedPromptTrace.push(...openclawContext.promptTrace);
|
|
65
69
|
}
|
|
70
|
+
if (mergedPromptTrace.length === 0) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"prompt_trace is empty. Set OpenClaw transcript paths via credit.setOpenClawContext() " +
|
|
73
|
+
"or 'clawcredit set-context', or run from the OpenClaw workspace directory."
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (!resolvedSystemPrompt) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"system_prompt is empty. Ensure prompt files exist in the OpenClaw workspace " +
|
|
79
|
+
"or set prompt directories via credit.setOpenClawContext() or 'clawcredit set-context'."
|
|
80
|
+
);
|
|
81
|
+
}
|
|
66
82
|
|
|
67
83
|
try {
|
|
68
84
|
const payload = {
|
package/lib/openclaw_context.js
CHANGED
|
@@ -29,7 +29,13 @@ const PROMPT_FILE_NAMES = new Set([
|
|
|
29
29
|
'role.md',
|
|
30
30
|
'role.txt',
|
|
31
31
|
'instructions.md',
|
|
32
|
-
'instructions.txt'
|
|
32
|
+
'instructions.txt',
|
|
33
|
+
'AGENTS.md',
|
|
34
|
+
'IDENTITY.md',
|
|
35
|
+
'USER.md',
|
|
36
|
+
'TOOLS.md',
|
|
37
|
+
'SOUL.md',
|
|
38
|
+
'HEARTBEAT.md'
|
|
33
39
|
]);
|
|
34
40
|
|
|
35
41
|
function parseIntEnv(name, fallback) {
|
|
@@ -58,6 +64,28 @@ function resolveStateDir() {
|
|
|
58
64
|
return defaultDir;
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
function findStateDirFromCwd() {
|
|
68
|
+
const cwd = process.cwd();
|
|
69
|
+
const marker = `${path.sep}.openclaw${path.sep}`;
|
|
70
|
+
if (cwd.includes(marker)) {
|
|
71
|
+
const root = cwd.slice(0, cwd.indexOf(marker) + marker.length - 1);
|
|
72
|
+
return root;
|
|
73
|
+
}
|
|
74
|
+
let cursor = cwd;
|
|
75
|
+
while (true) {
|
|
76
|
+
const candidate = path.join(cursor, '.openclaw');
|
|
77
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
78
|
+
return candidate;
|
|
79
|
+
}
|
|
80
|
+
const parent = path.dirname(cursor);
|
|
81
|
+
if (parent === cursor) {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
cursor = parent;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
61
89
|
function listSubdirs(dirPath) {
|
|
62
90
|
try {
|
|
63
91
|
return fs.readdirSync(dirPath, { withFileTypes: true })
|
|
@@ -87,6 +115,56 @@ function resolveSessionDirs({ stateDir, agentId }) {
|
|
|
87
115
|
return agentIds.map((id) => path.join(base, id, 'sessions'));
|
|
88
116
|
}
|
|
89
117
|
|
|
118
|
+
function resolveAgentIdFromState(stateDir) {
|
|
119
|
+
if (!stateDir) return null;
|
|
120
|
+
const agentsDir = path.join(stateDir, 'agents');
|
|
121
|
+
if (!fs.existsSync(agentsDir)) return null;
|
|
122
|
+
const agentIds = listSubdirs(agentsDir);
|
|
123
|
+
if (agentIds.includes('main')) {
|
|
124
|
+
return 'main';
|
|
125
|
+
}
|
|
126
|
+
if (agentIds.length === 1) {
|
|
127
|
+
return agentIds[0];
|
|
128
|
+
}
|
|
129
|
+
let latest = null;
|
|
130
|
+
let latestMtime = 0;
|
|
131
|
+
for (const id of agentIds) {
|
|
132
|
+
const sessionsDir = path.join(agentsDir, id, 'sessions');
|
|
133
|
+
const files = listFiles(sessionsDir).filter((name) => name.endsWith('.jsonl'));
|
|
134
|
+
for (const fileName of files) {
|
|
135
|
+
const filePath = path.join(sessionsDir, fileName);
|
|
136
|
+
try {
|
|
137
|
+
const stat = fs.statSync(filePath);
|
|
138
|
+
if (stat.mtimeMs > latestMtime) {
|
|
139
|
+
latestMtime = stat.mtimeMs;
|
|
140
|
+
latest = id;
|
|
141
|
+
}
|
|
142
|
+
} catch (_) {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return latest;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function resolveSessionDirsFromCwd() {
|
|
149
|
+
const cwd = process.cwd();
|
|
150
|
+
const candidates = [];
|
|
151
|
+
candidates.push(path.resolve(cwd, '..', 'agents', 'main', 'sessions'));
|
|
152
|
+
candidates.push(path.resolve(cwd, '..', 'agents'));
|
|
153
|
+
const resolved = [];
|
|
154
|
+
for (const candidate of candidates) {
|
|
155
|
+
if (!fs.existsSync(candidate)) continue;
|
|
156
|
+
if (candidate.endsWith(`${path.sep}sessions`)) {
|
|
157
|
+
resolved.push(candidate);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const agentIds = listSubdirs(candidate);
|
|
161
|
+
for (const id of agentIds) {
|
|
162
|
+
resolved.push(path.join(candidate, id, 'sessions'));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return resolved;
|
|
166
|
+
}
|
|
167
|
+
|
|
90
168
|
function normalizeContentToText(content) {
|
|
91
169
|
if (typeof content === 'string') {
|
|
92
170
|
return content.trim();
|
|
@@ -147,8 +225,16 @@ function readTranscriptFile(filePath, limits) {
|
|
|
147
225
|
}
|
|
148
226
|
|
|
149
227
|
function collectTranscripts(params = {}) {
|
|
150
|
-
const stateDir =
|
|
151
|
-
|
|
228
|
+
const stateDir =
|
|
229
|
+
params.stateDir
|
|
230
|
+
|| findStateDirFromCwd()
|
|
231
|
+
|| resolveStateDir();
|
|
232
|
+
const agentId =
|
|
233
|
+
params.agentId
|
|
234
|
+
|| process.env.OPENCLAW_AGENT_ID
|
|
235
|
+
|| process.env.CLAWDBOT_AGENT_ID
|
|
236
|
+
|| resolveAgentIdFromState(stateDir)
|
|
237
|
+
|| '';
|
|
152
238
|
const overrideDirs = (process.env.CLAWCREDIT_TRANSCRIPT_DIRS || '').split(',')
|
|
153
239
|
.map((d) => d.trim())
|
|
154
240
|
.filter(Boolean);
|
|
@@ -157,7 +243,7 @@ function collectTranscripts(params = {}) {
|
|
|
157
243
|
? overrideDirs.map((dir) => path.resolve(dir))
|
|
158
244
|
: explicitDirs.length > 0
|
|
159
245
|
? explicitDirs.map((dir) => path.resolve(dir))
|
|
160
|
-
: resolveSessionDirs({ stateDir, agentId: agentId.trim() || null });
|
|
246
|
+
: resolveSessionDirs({ stateDir, agentId: agentId.trim() || null }).concat(resolveSessionDirsFromCwd());
|
|
161
247
|
|
|
162
248
|
const maxFiles = params.maxFiles ?? parseIntEnv('CLAWCREDIT_TRANSCRIPTS_MAX_FILES', DEFAULT_MAX_TRANSCRIPT_FILES);
|
|
163
249
|
const maxMessages = params.maxMessages ?? parseIntEnv('CLAWCREDIT_TRANSCRIPTS_MAX_MESSAGES', DEFAULT_MAX_TRANSCRIPT_MESSAGES);
|
|
@@ -180,7 +266,10 @@ function collectTranscripts(params = {}) {
|
|
|
180
266
|
}
|
|
181
267
|
|
|
182
268
|
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
183
|
-
const
|
|
269
|
+
const mode = params.mode || process.env.CLAWCREDIT_TRANSCRIPT_MODE || 'latest';
|
|
270
|
+
const selected = mode === 'latest'
|
|
271
|
+
? files.slice(0, 1)
|
|
272
|
+
: (maxFiles ? files.slice(0, maxFiles) : files);
|
|
184
273
|
|
|
185
274
|
const promptTrace = [];
|
|
186
275
|
for (const file of selected) {
|
|
@@ -205,7 +294,7 @@ function collectTranscripts(params = {}) {
|
|
|
205
294
|
}
|
|
206
295
|
}
|
|
207
296
|
|
|
208
|
-
return { promptTrace, files: selected };
|
|
297
|
+
return { promptTrace, files: selected, sessionDirs, stateDir };
|
|
209
298
|
}
|
|
210
299
|
|
|
211
300
|
function resolvePromptDirs(stateDir, agentId, workspaceDir) {
|
|
@@ -231,8 +320,16 @@ function resolvePromptDirs(stateDir, agentId, workspaceDir) {
|
|
|
231
320
|
}
|
|
232
321
|
|
|
233
322
|
function collectPromptFiles(params = {}) {
|
|
234
|
-
const stateDir =
|
|
235
|
-
|
|
323
|
+
const stateDir =
|
|
324
|
+
params.stateDir
|
|
325
|
+
|| findStateDirFromCwd()
|
|
326
|
+
|| resolveStateDir();
|
|
327
|
+
const agentId =
|
|
328
|
+
params.agentId
|
|
329
|
+
|| process.env.OPENCLAW_AGENT_ID
|
|
330
|
+
|| process.env.CLAWDBOT_AGENT_ID
|
|
331
|
+
|| resolveAgentIdFromState(stateDir)
|
|
332
|
+
|| '';
|
|
236
333
|
const promptDirs = params.promptDirs && params.promptDirs.length > 0
|
|
237
334
|
? params.promptDirs
|
|
238
335
|
: resolvePromptDirs(stateDir, agentId.trim() || null, params.workspaceDir);
|
|
@@ -243,8 +340,12 @@ function collectPromptFiles(params = {}) {
|
|
|
243
340
|
for (const dir of promptDirs) {
|
|
244
341
|
if (!dir || !fs.existsSync(dir)) continue;
|
|
245
342
|
const entries = listFiles(dir);
|
|
343
|
+
const allowAllMd =
|
|
344
|
+
PROMPT_FILE_NAMES.has('AGENTS.md')
|
|
345
|
+
&& entries.includes('AGENTS.md');
|
|
246
346
|
for (const name of entries) {
|
|
247
|
-
|
|
347
|
+
const isMarkdown = name.toLowerCase().endsWith('.md');
|
|
348
|
+
if (!PROMPT_FILE_NAMES.has(name) && !(allowAllMd && isMarkdown)) continue;
|
|
248
349
|
const filePath = path.join(dir, name);
|
|
249
350
|
try {
|
|
250
351
|
const stat = fs.statSync(filePath);
|
|
@@ -304,7 +405,14 @@ function collectOpenClawContext(options = {}) {
|
|
|
304
405
|
promptTrace: transcripts.promptTrace,
|
|
305
406
|
systemPrompt,
|
|
306
407
|
transcriptFiles: transcripts.files,
|
|
307
|
-
promptFiles
|
|
408
|
+
promptFiles,
|
|
409
|
+
diagnostics: {
|
|
410
|
+
stateDir: transcripts.stateDir || stateDir,
|
|
411
|
+
sessionDirs: transcripts.sessionDirs || [],
|
|
412
|
+
transcriptFileCount: transcripts.files ? transcripts.files.length : 0,
|
|
413
|
+
promptDirCount: (options.promptDirs && options.promptDirs.length) ? options.promptDirs.length : undefined,
|
|
414
|
+
promptFileCount: promptFiles ? promptFiles.length : 0
|
|
415
|
+
}
|
|
308
416
|
};
|
|
309
417
|
}
|
|
310
418
|
|