@consensus-tools/consensus-tools 0.1.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/LICENSE +201 -0
- package/README.md +434 -0
- package/index.ts +243 -0
- package/openclaw.plugin.json +185 -0
- package/package.json +28 -0
- package/src/cli.ts +954 -0
- package/src/config.ts +306 -0
- package/src/jobs/consensus.ts +191 -0
- package/src/jobs/eligibility.ts +11 -0
- package/src/jobs/engine.ts +503 -0
- package/src/jobs/slashing.ts +11 -0
- package/src/ledger/ledger.ts +140 -0
- package/src/ledger/rules.ts +19 -0
- package/src/network/client.ts +70 -0
- package/src/network/server.ts +143 -0
- package/src/service.ts +26 -0
- package/src/storage/IStorage.ts +31 -0
- package/src/storage/JsonStorage.ts +63 -0
- package/src/storage/SqliteStorage.ts +67 -0
- package/src/testing/consensusTestRunner.ts +0 -0
- package/src/tools.ts +184 -0
- package/src/types.ts +232 -0
- package/src/util/ids.ts +21 -0
- package/src/util/locks.ts +36 -0
- package/src/util/table.ts +35 -0
- package/src/util/time.ts +12 -0
package/index.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { PLUGIN_ID, loadConfig, resolveAgentId, validateConfig } from './src/config';
|
|
2
|
+
import { createStorage } from './src/storage/IStorage';
|
|
3
|
+
import { LedgerEngine } from './src/ledger/ledger';
|
|
4
|
+
import { JobEngine } from './src/jobs/engine';
|
|
5
|
+
import { ConsensusToolsClient } from './src/network/client';
|
|
6
|
+
import { ConsensusToolsServer } from './src/network/server';
|
|
7
|
+
import { registerCli } from './src/cli';
|
|
8
|
+
import { registerTools } from './src/tools';
|
|
9
|
+
import { createService } from './src/service';
|
|
10
|
+
import type { ConsensusToolsConfig, Job } from './src/types';
|
|
11
|
+
|
|
12
|
+
export default async function register(api: any) {
|
|
13
|
+
const logger = api?.logger?.child ? api.logger.child({ plugin: PLUGIN_ID }) : api?.logger;
|
|
14
|
+
const loaded = loadConfig(api, logger);
|
|
15
|
+
const { config } = validateConfig(loaded, logger);
|
|
16
|
+
const agentId = resolveAgentId(api, config);
|
|
17
|
+
|
|
18
|
+
const storage = createStorage(config);
|
|
19
|
+
await storage.init();
|
|
20
|
+
|
|
21
|
+
const ledger = new LedgerEngine(storage, config, logger);
|
|
22
|
+
const engine = new JobEngine(storage, ledger, config, logger);
|
|
23
|
+
const server = new ConsensusToolsServer(config, engine, ledger, logger);
|
|
24
|
+
const client = new ConsensusToolsClient(config.global.baseUrl, config.global.accessToken, logger);
|
|
25
|
+
|
|
26
|
+
if (config.mode === 'local') {
|
|
27
|
+
await ledger.applyConfigBalances(
|
|
28
|
+
config.local.ledger.balances,
|
|
29
|
+
config.local.ledger.balancesMode ?? 'initial'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const backend = createBackend(config, engine, ledger, client, server, storage, logger, agentId);
|
|
34
|
+
|
|
35
|
+
api.registerCli(
|
|
36
|
+
({ program }: any) => {
|
|
37
|
+
registerCli(program, backend, config, agentId);
|
|
38
|
+
},
|
|
39
|
+
{ commands: ['consensus'] }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
registerTools(api, backend, config, agentId);
|
|
43
|
+
|
|
44
|
+
const capabilities = api?.capabilities || api?.agent?.capabilities || [];
|
|
45
|
+
const serviceBackend = {
|
|
46
|
+
listJobs: backend.listJobs,
|
|
47
|
+
claimJob: backend.claimJob,
|
|
48
|
+
heartbeat: backend.heartbeat,
|
|
49
|
+
getJob: backend.getJob
|
|
50
|
+
};
|
|
51
|
+
const service = createService(config, serviceBackend, agentId, capabilities, logger);
|
|
52
|
+
api.registerService({ id: 'consensus-tools', start: service.start, stop: service.stop });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createBackend(
|
|
56
|
+
config: ConsensusToolsConfig,
|
|
57
|
+
engine: JobEngine,
|
|
58
|
+
ledger: LedgerEngine,
|
|
59
|
+
client: ConsensusToolsClient,
|
|
60
|
+
server: ConsensusToolsServer,
|
|
61
|
+
storage: any,
|
|
62
|
+
logger: any,
|
|
63
|
+
agentId: string
|
|
64
|
+
) {
|
|
65
|
+
const ensureNetworkSideEffects = (action: string) => {
|
|
66
|
+
if (config.mode === 'global' && !config.global.accessToken) {
|
|
67
|
+
throw new Error('Global access token missing. Set plugins.entries.consensus-tools.config.global.accessToken.');
|
|
68
|
+
}
|
|
69
|
+
if (config.mode === 'global' && !config.safety.allowNetworkSideEffects) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Network side effects disabled. Enable plugins.entries.consensus-tools.config.safety.allowNetworkSideEffects to ${action}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const recordLocalError = async (err: any, context: Record<string, unknown>) => {
|
|
77
|
+
if (config.mode !== 'local') return;
|
|
78
|
+
try {
|
|
79
|
+
await engine.recordError(err?.message || 'Error', context);
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const ensureGlobalAccess = (action: string) => {
|
|
86
|
+
if (config.mode === 'global' && !config.global.accessToken) {
|
|
87
|
+
throw new Error(`Global access token missing. Set plugins.entries.consensus-tools.config.global.accessToken to ${action}.`);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const backend = {
|
|
92
|
+
postJob: async (actorId: string, input: any): Promise<Job> => {
|
|
93
|
+
if (config.mode === 'global') {
|
|
94
|
+
ensureGlobalAccess('post jobs');
|
|
95
|
+
ensureNetworkSideEffects('post jobs');
|
|
96
|
+
return client.postJob(actorId, input);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
return await engine.postJob(actorId, input);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
await recordLocalError(err, { action: 'postJob', actorId });
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
listJobs: async (filters?: Record<string, string | undefined>): Promise<Job[]> => {
|
|
106
|
+
if (config.mode === 'global') {
|
|
107
|
+
ensureGlobalAccess('list jobs');
|
|
108
|
+
return client.listJobs(filters || {});
|
|
109
|
+
}
|
|
110
|
+
return engine.listJobs(filters || {});
|
|
111
|
+
},
|
|
112
|
+
getJob: async (jobId: string): Promise<Job | undefined> => {
|
|
113
|
+
if (config.mode === 'global') {
|
|
114
|
+
ensureGlobalAccess('get jobs');
|
|
115
|
+
return client.getJob(jobId);
|
|
116
|
+
}
|
|
117
|
+
return engine.getJob(jobId);
|
|
118
|
+
},
|
|
119
|
+
getStatus: async (jobId: string): Promise<any> => {
|
|
120
|
+
if (config.mode === 'global') {
|
|
121
|
+
ensureGlobalAccess('get job status');
|
|
122
|
+
return client.getStatus(jobId);
|
|
123
|
+
}
|
|
124
|
+
return engine.getStatus(jobId);
|
|
125
|
+
},
|
|
126
|
+
claimJob: async (actorId: string, jobId: string, stakeAmount: number, leaseSeconds: number): Promise<any> => {
|
|
127
|
+
if (config.mode === 'global') {
|
|
128
|
+
ensureGlobalAccess('claim jobs');
|
|
129
|
+
ensureNetworkSideEffects('claim jobs');
|
|
130
|
+
return client.claimJob(actorId, jobId, { stakeAmount, leaseSeconds });
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
return await engine.claimJob(actorId, jobId, { stakeAmount, leaseSeconds });
|
|
134
|
+
} catch (err) {
|
|
135
|
+
await recordLocalError(err, { action: 'claimJob', actorId, jobId });
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
heartbeat: async (actorId: string, jobId: string): Promise<void> => {
|
|
140
|
+
if (config.mode === 'global') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
return engine.heartbeat(actorId, jobId);
|
|
144
|
+
},
|
|
145
|
+
submitJob: async (actorId: string, jobId: string, input: any): Promise<any> => {
|
|
146
|
+
if (config.mode === 'global') {
|
|
147
|
+
ensureGlobalAccess('submit jobs');
|
|
148
|
+
ensureNetworkSideEffects('submit jobs');
|
|
149
|
+
return client.submitJob(actorId, jobId, input);
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return await engine.submitJob(actorId, jobId, input);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
await recordLocalError(err, { action: 'submitJob', actorId, jobId });
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
listSubmissions: async (jobId: string): Promise<any[]> => {
|
|
159
|
+
if (config.mode === 'global') {
|
|
160
|
+
ensureGlobalAccess('list submissions');
|
|
161
|
+
const status = await client.getStatus(jobId);
|
|
162
|
+
return status?.submissions || [];
|
|
163
|
+
}
|
|
164
|
+
const status = await engine.getStatus(jobId);
|
|
165
|
+
return status.submissions;
|
|
166
|
+
},
|
|
167
|
+
listVotes: async (jobId: string): Promise<any[]> => {
|
|
168
|
+
if (config.mode === 'global') {
|
|
169
|
+
ensureGlobalAccess('list votes');
|
|
170
|
+
const status = await client.getStatus(jobId);
|
|
171
|
+
return status?.votes || [];
|
|
172
|
+
}
|
|
173
|
+
const status = await engine.getStatus(jobId);
|
|
174
|
+
return status.votes;
|
|
175
|
+
},
|
|
176
|
+
vote: async (actorId: string, jobId: string, input: any): Promise<any> => {
|
|
177
|
+
if (config.mode === 'global') {
|
|
178
|
+
ensureGlobalAccess('vote');
|
|
179
|
+
ensureNetworkSideEffects('vote');
|
|
180
|
+
return client.vote(actorId, jobId, input);
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
return await engine.vote(actorId, jobId, input);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
await recordLocalError(err, { action: 'vote', actorId, jobId });
|
|
186
|
+
throw err;
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
resolveJob: async (actorId: string, jobId: string, input: any): Promise<any> => {
|
|
190
|
+
if (config.mode === 'global') {
|
|
191
|
+
ensureGlobalAccess('resolve jobs');
|
|
192
|
+
ensureNetworkSideEffects('resolve jobs');
|
|
193
|
+
return client.resolveJob(actorId, jobId, input);
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
return await engine.resolveJob(actorId, jobId, input);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
await recordLocalError(err, { action: 'resolveJob', actorId, jobId });
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
getLedgerBalance: async (target: string): Promise<number> => {
|
|
203
|
+
if (config.mode === 'global') {
|
|
204
|
+
ensureGlobalAccess('read ledger');
|
|
205
|
+
const result = await client.getLedger(target);
|
|
206
|
+
return result.balance;
|
|
207
|
+
}
|
|
208
|
+
return ledger.getBalance(target);
|
|
209
|
+
},
|
|
210
|
+
faucet: async (target: string, amount: number): Promise<any> => {
|
|
211
|
+
if (!config.local.ledger.faucetEnabled) {
|
|
212
|
+
throw new Error('Faucet disabled');
|
|
213
|
+
}
|
|
214
|
+
if (config.mode === 'global') {
|
|
215
|
+
ensureGlobalAccess('use faucet');
|
|
216
|
+
ensureNetworkSideEffects('use faucet');
|
|
217
|
+
throw new Error('Faucet not available over global mode');
|
|
218
|
+
}
|
|
219
|
+
return ledger.faucet(target, amount, `faucet:${agentId}`);
|
|
220
|
+
},
|
|
221
|
+
getDiagnostics: async () => {
|
|
222
|
+
const errors = (await storage.getState()).errors.map((err: any) => ({ at: err.at, message: err.message }));
|
|
223
|
+
let networkOk: boolean | undefined = undefined;
|
|
224
|
+
if (config.mode === 'global') {
|
|
225
|
+
try {
|
|
226
|
+
if (!config.global.accessToken) {
|
|
227
|
+
throw new Error('Global access token missing.');
|
|
228
|
+
}
|
|
229
|
+
await client.listJobs({});
|
|
230
|
+
networkOk = true;
|
|
231
|
+
} catch (err) {
|
|
232
|
+
logger?.warn?.({ err }, 'consensus-tools: diagnostics network check failed');
|
|
233
|
+
networkOk = false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { errors, networkOk };
|
|
237
|
+
},
|
|
238
|
+
startServer: async () => server.start(),
|
|
239
|
+
stopServer: async () => server.stop()
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return backend;
|
|
243
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "consensus-tools",
|
|
3
|
+
"name": "consensus-tools",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"description": "consensus-tools distributed job board for OpenClaw agents",
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"properties": {
|
|
11
|
+
"mode": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"enum": ["local", "global"],
|
|
14
|
+
"default": "local",
|
|
15
|
+
"description": "Choose local storage or a hosted global job board"
|
|
16
|
+
},
|
|
17
|
+
"local": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"properties": {
|
|
21
|
+
"storage": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"properties": {
|
|
25
|
+
"kind": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"enum": ["sqlite", "json"],
|
|
28
|
+
"default": "json"
|
|
29
|
+
},
|
|
30
|
+
"path": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"default": "./.openclaw/consensus-tools.json"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": ["kind", "path"]
|
|
36
|
+
},
|
|
37
|
+
"server": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"properties": {
|
|
41
|
+
"enabled": { "type": "boolean", "default": false },
|
|
42
|
+
"host": { "type": "string", "default": "127.0.0.1" },
|
|
43
|
+
"port": { "type": "integer", "default": 9888, "minimum": 1, "maximum": 65535 },
|
|
44
|
+
"authToken": { "type": "string", "default": "" }
|
|
45
|
+
},
|
|
46
|
+
"required": ["enabled", "host", "port", "authToken"]
|
|
47
|
+
},
|
|
48
|
+
"slashingEnabled": { "type": "boolean", "default": false },
|
|
49
|
+
"jobDefaults": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"reward": { "type": "number", "default": 10, "minimum": 0 },
|
|
54
|
+
"stakeRequired": { "type": "number", "default": 1, "minimum": 0 },
|
|
55
|
+
"maxParticipants": { "type": "integer", "default": 3, "minimum": 1 },
|
|
56
|
+
"minParticipants": { "type": "integer", "default": 1, "minimum": 1 },
|
|
57
|
+
"expiresSeconds": { "type": "integer", "default": 86400, "minimum": 60 },
|
|
58
|
+
"consensusPolicy": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"properties": {
|
|
62
|
+
"type": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"enum": ["SINGLE_WINNER", "MAJORITY_VOTE", "WEIGHTED_REPUTATION", "TRUSTED_ARBITER"],
|
|
65
|
+
"default": "SINGLE_WINNER"
|
|
66
|
+
},
|
|
67
|
+
"trustedArbiterAgentId": { "type": "string", "default": "" }
|
|
68
|
+
},
|
|
69
|
+
"required": ["type", "trustedArbiterAgentId"]
|
|
70
|
+
},
|
|
71
|
+
"slashingPolicy": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"properties": {
|
|
75
|
+
"enabled": { "type": "boolean", "default": false },
|
|
76
|
+
"slashPercent": { "type": "number", "default": 0, "minimum": 0, "maximum": 1 },
|
|
77
|
+
"slashFlat": { "type": "number", "default": 0, "minimum": 0 }
|
|
78
|
+
},
|
|
79
|
+
"required": ["enabled", "slashPercent", "slashFlat"]
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"required": [
|
|
83
|
+
"reward",
|
|
84
|
+
"stakeRequired",
|
|
85
|
+
"maxParticipants",
|
|
86
|
+
"minParticipants",
|
|
87
|
+
"expiresSeconds",
|
|
88
|
+
"consensusPolicy",
|
|
89
|
+
"slashingPolicy"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"ledger": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"faucetEnabled": { "type": "boolean", "default": false },
|
|
97
|
+
"initialCreditsPerAgent": { "type": "number", "default": 0, "minimum": 0 },
|
|
98
|
+
"balancesMode": { "type": "string", "enum": ["initial", "override"], "default": "initial" },
|
|
99
|
+
"balances": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"additionalProperties": { "type": "number", "minimum": 0 },
|
|
102
|
+
"default": {}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"required": ["faucetEnabled", "initialCreditsPerAgent", "balancesMode", "balances"]
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"required": ["storage", "server", "slashingEnabled", "jobDefaults", "ledger"]
|
|
109
|
+
},
|
|
110
|
+
"global": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"properties": {
|
|
114
|
+
"baseUrl": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"default": "http://localhost:9888"
|
|
117
|
+
},
|
|
118
|
+
"accessToken": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"default": ""
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"required": ["baseUrl", "accessToken"]
|
|
124
|
+
},
|
|
125
|
+
"agentIdentity": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"properties": {
|
|
129
|
+
"agentIdSource": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"enum": ["openclaw", "env", "manual"],
|
|
132
|
+
"default": "openclaw"
|
|
133
|
+
},
|
|
134
|
+
"manualAgentId": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"default": ""
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"required": ["agentIdSource", "manualAgentId"]
|
|
140
|
+
},
|
|
141
|
+
"safety": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"additionalProperties": false,
|
|
144
|
+
"properties": {
|
|
145
|
+
"requireOptionalToolsOptIn": { "type": "boolean", "default": true },
|
|
146
|
+
"allowNetworkSideEffects": { "type": "boolean", "default": false }
|
|
147
|
+
},
|
|
148
|
+
"required": ["requireOptionalToolsOptIn", "allowNetworkSideEffects"]
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"required": ["mode", "agentIdentity", "safety"],
|
|
152
|
+
"allOf": [
|
|
153
|
+
{
|
|
154
|
+
"if": { "properties": { "mode": { "const": "local" } } },
|
|
155
|
+
"then": { "required": ["local"] }
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"if": { "properties": { "mode": { "const": "global" } } },
|
|
159
|
+
"then": { "required": ["global"] }
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
"uiHints": {
|
|
164
|
+
"local": {
|
|
165
|
+
"storage": {
|
|
166
|
+
"path": { "ui:widget": "file" }
|
|
167
|
+
},
|
|
168
|
+
"server": {
|
|
169
|
+
"authToken": { "ui:widget": "password", "sensitive": true }
|
|
170
|
+
},
|
|
171
|
+
"ledger": {
|
|
172
|
+
"faucetEnabled": { "ui:widget": "checkbox", "warning": "Dev-only faucet" },
|
|
173
|
+
"balancesMode": { "label": "Balance mode", "ui:widget": "radio" },
|
|
174
|
+
"balances": { "label": "Per-agent balances" }
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"global": {
|
|
178
|
+
"baseUrl": { "label": "Global board URL" },
|
|
179
|
+
"accessToken": { "ui:widget": "password", "sensitive": true, "label": "Access Token" }
|
|
180
|
+
},
|
|
181
|
+
"safety": {
|
|
182
|
+
"allowNetworkSideEffects": { "ui:widget": "checkbox", "warning": "Enables network mutations" }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@consensus-tools/consensus-tools",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.ts",
|
|
6
|
+
"openclaw": {
|
|
7
|
+
"extensions": ["./index.ts"]
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "tsx --test"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.ts",
|
|
14
|
+
"src",
|
|
15
|
+
"openclaw.plugin.json",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"ajv": "^8.12.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"openai": "^4.79.0",
|
|
23
|
+
"tsx": "^4.19.2"
|
|
24
|
+
},
|
|
25
|
+
"optionalDependencies": {
|
|
26
|
+
"better-sqlite3": "^9.4.3"
|
|
27
|
+
}
|
|
28
|
+
}
|