@halot/cli 1.0.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/.env.example +14 -0
- package/README.md +374 -0
- package/dist/features/browse/browse.command.js +15 -0
- package/dist/features/jobs/job-create.command.js +57 -0
- package/dist/features/jobs/job-funding.service.js +95 -0
- package/dist/features/jobs/job-result.command.js +10 -0
- package/dist/features/jobs/job-watch.command.js +18 -0
- package/dist/features/provider/provider-init.command.js +33 -0
- package/dist/features/provider/provider-register.command.js +72 -0
- package/dist/features/provider/provider-run.command.js +148 -0
- package/dist/features/provider/provider-services.command.js +13 -0
- package/dist/features/provider/provider-stats.command.js +13 -0
- package/dist/features/provider/provider-update.command.js +35 -0
- package/dist/features/quotes/quote.command.js +13 -0
- package/dist/features/requester/requester-init.command.js +22 -0
- package/dist/features/service/service-create.command.js +57 -0
- package/dist/features/service/service-init.command.js +89 -0
- package/dist/features/service/service-list.command.js +23 -0
- package/dist/features/service/service-remove.command.js +21 -0
- package/dist/features/service/service-update.command.js +65 -0
- package/dist/features/verifier/verifier-init.command.js +69 -0
- package/dist/features/verifier/verifier-register.command.js +90 -0
- package/dist/features/verifier/verifier-run.command.js +143 -0
- package/dist/features/verifier/verifier-stats.command.js +13 -0
- package/dist/features/verifier/verifier-unstake.command.js +31 -0
- package/dist/index.js +136 -0
- package/dist/shared/auth/actor-headers.service.js +13 -0
- package/dist/shared/config/env.js +77 -0
- package/dist/shared/http/api-client.js +66 -0
- package/dist/shared/http/event-stream.js +75 -0
- package/dist/shared/integrations/agent-id/agent-id-register.service.js +159 -0
- package/dist/shared/integrations/space-id/space-id-register.service.js +467 -0
- package/dist/shared/integrations/zero-g/verifier-registry.service.js +55 -0
- package/dist/shared/output/print.util.js +10 -0
- package/dist/shared/wallet/authority.util.js +62 -0
- package/dist/shared/wallet/wallet.manager.js +41 -0
- package/dist/shared/workspace/workspace.service.js +192 -0
- package/package.json +47 -0
package/.env.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
HALOT_SERVER_URL=http://localhost:3001
|
|
2
|
+
HALOT_SPACE_ID_IDENTIFIER=
|
|
3
|
+
HALOT_SPACE_ID_CONTROLLER_ADDRESS=
|
|
4
|
+
HALOT_SPACE_ID_RESOLVER_ADDRESS=
|
|
5
|
+
HALOT_SPACE_ID_SIMULATE_ACCOUNT=
|
|
6
|
+
HALOT_SPACE_ID_SIMULATE_VALUE=
|
|
7
|
+
HALOT_SPACE_ID_GRAPH_URL=
|
|
8
|
+
HALOT_SPACE_ID_VERIFIED_TLD_HUB_ADDRESS=
|
|
9
|
+
HALOT_SPACE_ID_VERIFIED_TLD_HUB_RPC_URL=
|
|
10
|
+
HALOT_ZERO_G_STORAGE_GATEWAY=
|
|
11
|
+
HALOT_AGENT_ID_REGISTRY_ADDRESS=
|
|
12
|
+
HALOT_ZERO_G_VERIFIER_REGISTRY_ADDRESS=
|
|
13
|
+
HALOT_DEFAULT_STELLAR_RPC_URL=
|
|
14
|
+
HALOT_DEFAULT_STELLAR_NETWORK_PASSPHRASE=Public Global Stellar Network ; September 2015
|
package/README.md
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# halot
|
|
2
|
+
|
|
3
|
+
`halot` is the CLI for initializing workspaces and operating against the Halot network.
|
|
4
|
+
|
|
5
|
+
It covers:
|
|
6
|
+
|
|
7
|
+
- requester setup and job creation
|
|
8
|
+
- provider registration and updates
|
|
9
|
+
- service drafting and registration
|
|
10
|
+
- verifier registration, runtime, and staking
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g halot
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
or run it without a global install:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx halot --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Provider Flow
|
|
25
|
+
|
|
26
|
+
### 1. Initialize
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
halot provider init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This creates:
|
|
33
|
+
|
|
34
|
+
- `wallets.json`
|
|
35
|
+
- `halot.provider.json`
|
|
36
|
+
|
|
37
|
+
`wallets.json` stores network-scoped private keys:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"authorities": {
|
|
42
|
+
"0g:testnet": {
|
|
43
|
+
"privateKey": "0x..."
|
|
44
|
+
},
|
|
45
|
+
"0g:mainnet": {
|
|
46
|
+
"privateKey": "0x..."
|
|
47
|
+
},
|
|
48
|
+
"stellar:testnet": {
|
|
49
|
+
"privateKey": "S..."
|
|
50
|
+
},
|
|
51
|
+
"stellar:mainnet": {
|
|
52
|
+
"privateKey": "S..."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`halot.provider.json` stores provider identity, the selected Halot actor authority, and settlement wallets:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"providerId": "",
|
|
63
|
+
"displayName": "My Provider",
|
|
64
|
+
"description": "Provider description",
|
|
65
|
+
"version": "1.0.0",
|
|
66
|
+
"identity": {
|
|
67
|
+
"domain": "myprovider.0g",
|
|
68
|
+
"domainType": "space-id",
|
|
69
|
+
"ownerAddress": "0x...",
|
|
70
|
+
"erc8004TokenId": "",
|
|
71
|
+
"agentRegistry": "",
|
|
72
|
+
"agentUri": ""
|
|
73
|
+
},
|
|
74
|
+
"authority": {
|
|
75
|
+
"path": "./wallets.json",
|
|
76
|
+
"actor": "0g:testnet"
|
|
77
|
+
},
|
|
78
|
+
"settlementWallets": {
|
|
79
|
+
"0g:testnet": "0x...",
|
|
80
|
+
"0g:mainnet": "0x...",
|
|
81
|
+
"stellar:testnet": "G...",
|
|
82
|
+
"stellar:mainnet": "G..."
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Register
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
halot provider register
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This:
|
|
94
|
+
|
|
95
|
+
- reads `halot.provider.json`
|
|
96
|
+
- signs with `authority.actor` from `wallets.json`
|
|
97
|
+
- uploads the provider config to 0G Storage
|
|
98
|
+
- anchors the root hash on the 0G provider registry
|
|
99
|
+
- writes back the canonical `providerId`
|
|
100
|
+
|
|
101
|
+
On mainnet, provider registration also performs:
|
|
102
|
+
|
|
103
|
+
- SPACE ID `.0g` registration
|
|
104
|
+
- Agent ID / ERC-8004 registration
|
|
105
|
+
|
|
106
|
+
### 3. Update
|
|
107
|
+
|
|
108
|
+
After editing mutable provider fields such as:
|
|
109
|
+
|
|
110
|
+
- `displayName`
|
|
111
|
+
- `description`
|
|
112
|
+
- `version`
|
|
113
|
+
- `settlementWallets`
|
|
114
|
+
|
|
115
|
+
run:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
halot provider update
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 4. Run
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
halot provider run
|
|
125
|
+
halot provider run --once
|
|
126
|
+
halot provider run --interval 5000
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The worker opens an assignment stream, forwards jobs to the assigned service `endpoint.execution`, and submits results back to Halot.
|
|
130
|
+
|
|
131
|
+
If you are using SDK middleware instead of the worker path, you do not need `halot provider run`.
|
|
132
|
+
|
|
133
|
+
## Service Flow
|
|
134
|
+
|
|
135
|
+
Services are drafted separately from provider registration.
|
|
136
|
+
|
|
137
|
+
### 1. Create a draft
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
halot service init --name "GPT-5.4 Text"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This creates a local draft such as `services/service-001.json`.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"serviceId": "",
|
|
150
|
+
"name": "GPT-5.4 Text",
|
|
151
|
+
"description": "Returns paid text output for a prompt",
|
|
152
|
+
"version": "1.0.0",
|
|
153
|
+
"category": "text",
|
|
154
|
+
"trustLevel": "standard",
|
|
155
|
+
"endpoint": {
|
|
156
|
+
"health": "https://your-provider.example/health",
|
|
157
|
+
"execution": "https://your-provider.example/execute"
|
|
158
|
+
},
|
|
159
|
+
"pricing": {
|
|
160
|
+
"baseFee": "3.00",
|
|
161
|
+
"currency": "USDC",
|
|
162
|
+
"model": "per-job"
|
|
163
|
+
},
|
|
164
|
+
"settlement": {
|
|
165
|
+
"accepts": [
|
|
166
|
+
{
|
|
167
|
+
"network": "0g:testnet",
|
|
168
|
+
"token": "USDC"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"network": "stellar:testnet",
|
|
172
|
+
"token": "USDC"
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
"input": {
|
|
177
|
+
"schema": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"query": { "type": "string", "maxLength": 500 }
|
|
181
|
+
},
|
|
182
|
+
"required": ["query"]
|
|
183
|
+
},
|
|
184
|
+
"maxSize": "10kb"
|
|
185
|
+
},
|
|
186
|
+
"output": {
|
|
187
|
+
"schema": {
|
|
188
|
+
"type": "object",
|
|
189
|
+
"properties": {
|
|
190
|
+
"text": { "type": "string" }
|
|
191
|
+
},
|
|
192
|
+
"required": ["text"]
|
|
193
|
+
},
|
|
194
|
+
"format": "json"
|
|
195
|
+
},
|
|
196
|
+
"acceptanceCriteria": {
|
|
197
|
+
"schemaValid": true
|
|
198
|
+
},
|
|
199
|
+
"sla": {
|
|
200
|
+
"maxExecutionTime": 120,
|
|
201
|
+
"retryPolicy": "none"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 2. Register
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
halot service register
|
|
210
|
+
halot service register --file service-001.json
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
This:
|
|
214
|
+
|
|
215
|
+
- reads `providerId` from `halot.provider.json`
|
|
216
|
+
- signs as that provider from `wallets.json`
|
|
217
|
+
- uploads the service definition to 0G Storage
|
|
218
|
+
- anchors it on the provider registry
|
|
219
|
+
- rewrites the file with the canonical `serviceId`
|
|
220
|
+
|
|
221
|
+
### 3. Manage services
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
halot service list
|
|
225
|
+
halot service update <serviceId>
|
|
226
|
+
halot service update --file service-svc_abc123.json
|
|
227
|
+
halot service remove <serviceId>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Current categories:
|
|
231
|
+
|
|
232
|
+
- `text`
|
|
233
|
+
- `code`
|
|
234
|
+
- `image`
|
|
235
|
+
- `audio`
|
|
236
|
+
- `video`
|
|
237
|
+
- `document`
|
|
238
|
+
- `tool`
|
|
239
|
+
- `workflow`
|
|
240
|
+
|
|
241
|
+
## Verifier Flow
|
|
242
|
+
|
|
243
|
+
### 1. Initialize
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
halot verifier init
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
This creates:
|
|
250
|
+
|
|
251
|
+
- `wallets.json`
|
|
252
|
+
- `halot.verifier.json`
|
|
253
|
+
- `model.config.json`
|
|
254
|
+
- `inference.json`
|
|
255
|
+
- `specializations.json`
|
|
256
|
+
|
|
257
|
+
`halot.verifier.json`:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"verifierId": "",
|
|
262
|
+
"displayName": "My Verifier",
|
|
263
|
+
"description": "Multimodal service verification specialist",
|
|
264
|
+
"identity": {
|
|
265
|
+
"domain": "myverifier.0g",
|
|
266
|
+
"domainType": "space-id",
|
|
267
|
+
"ownerAddress": "0x...",
|
|
268
|
+
"erc8004TokenId": "",
|
|
269
|
+
"agentRegistry": "",
|
|
270
|
+
"agentUri": ""
|
|
271
|
+
},
|
|
272
|
+
"authority": {
|
|
273
|
+
"path": "./wallets.json",
|
|
274
|
+
"actor": "0g:testnet"
|
|
275
|
+
},
|
|
276
|
+
"settlementWallets": {
|
|
277
|
+
"0g:testnet": "0x...",
|
|
278
|
+
"0g:mainnet": "0x...",
|
|
279
|
+
"stellar:testnet": "G...",
|
|
280
|
+
"stellar:mainnet": "G..."
|
|
281
|
+
},
|
|
282
|
+
"stake": {
|
|
283
|
+
"amount": "1000",
|
|
284
|
+
"token": "0G",
|
|
285
|
+
"status": "unlocked"
|
|
286
|
+
},
|
|
287
|
+
"fees": {
|
|
288
|
+
"computeBudgetPerJob": "0.50"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`model.config.json`:
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"evaluationModel": {
|
|
298
|
+
"providerAddress": "0x...",
|
|
299
|
+
"serviceType": "chatbot",
|
|
300
|
+
"verifiability": "TeeML"
|
|
301
|
+
},
|
|
302
|
+
"sdk": {
|
|
303
|
+
"network": "testnet",
|
|
304
|
+
"rpcUrl": "https://evmrpc-testnet.0g.ai"
|
|
305
|
+
},
|
|
306
|
+
"timeout": 60
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`inference.json`:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"systemPrompt": "You are an impartial evaluator.",
|
|
315
|
+
"promptTemplate": "Evaluate whether the provider result and any referenced artifacts satisfy the acceptance criteria.",
|
|
316
|
+
"outputSchema": {
|
|
317
|
+
"decision": "approve | reject | uncertain",
|
|
318
|
+
"confidence": "number between 0 and 1",
|
|
319
|
+
"reason": "string",
|
|
320
|
+
"failedCriteria": "array"
|
|
321
|
+
},
|
|
322
|
+
"maxTokens": 1024,
|
|
323
|
+
"temperature": 0
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`specializations.json`:
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"categories": ["text", "document", "tool"],
|
|
332
|
+
"serviceIds": [],
|
|
333
|
+
"maxConcurrentJobs": 5,
|
|
334
|
+
"minConfidenceThreshold": 0.75
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 2. Register
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
halot verifier register
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
This:
|
|
345
|
+
|
|
346
|
+
- registers the verifier with Halot
|
|
347
|
+
- locks native 0G stake in the verifier registry
|
|
348
|
+
- runs mainnet identity steps when applicable
|
|
349
|
+
|
|
350
|
+
### 3. Run and unstake
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
halot verifier run
|
|
354
|
+
halot verifier run --once
|
|
355
|
+
halot verifier stats
|
|
356
|
+
halot verifier unstake
|
|
357
|
+
halot verifier unstake --claim
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Requester and Jobs
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
halot requester init
|
|
364
|
+
halot quote --service svc_... --input '{"query":"hello"}'
|
|
365
|
+
halot job create --service svc_... --input '{"query":"hello"}'
|
|
366
|
+
halot job watch <jobId>
|
|
367
|
+
halot job result <jobId>
|
|
368
|
+
halot browse
|
|
369
|
+
halot browse --service <serviceId>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Security
|
|
373
|
+
|
|
374
|
+
`wallets.json` contains raw private keys. Do not commit it to version control.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runBrowseCommand = runBrowseCommand;
|
|
4
|
+
const api_client_1 = require("@halot/cli/shared/http/api-client");
|
|
5
|
+
const print_util_1 = require("@halot/cli/shared/output/print.util");
|
|
6
|
+
async function runBrowseCommand(options) {
|
|
7
|
+
const api = new api_client_1.ApiClient(options.server);
|
|
8
|
+
if (options.service) {
|
|
9
|
+
const service = await api.get(`/services/${options.service}`);
|
|
10
|
+
(0, print_util_1.printJson)(service);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const providers = await api.get('/providers');
|
|
14
|
+
(0, print_util_1.printJson)(providers);
|
|
15
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runJobCreateCommand = runJobCreateCommand;
|
|
4
|
+
const sdk_1 = require("@halot/sdk");
|
|
5
|
+
const api_client_1 = require("@halot/cli/shared/http/api-client");
|
|
6
|
+
const print_util_1 = require("@halot/cli/shared/output/print.util");
|
|
7
|
+
const authority_util_1 = require("@halot/cli/shared/wallet/authority.util");
|
|
8
|
+
const workspace_service_1 = require("@halot/cli/shared/workspace/workspace.service");
|
|
9
|
+
const job_funding_service_1 = require("@halot/cli/features/jobs/job-funding.service");
|
|
10
|
+
function encodePaymentSignature(authorization) {
|
|
11
|
+
return Buffer.from(JSON.stringify(authorization)).toString('base64');
|
|
12
|
+
}
|
|
13
|
+
async function runJobCreateCommand(options) {
|
|
14
|
+
const workspace = new workspace_service_1.WorkspaceService();
|
|
15
|
+
const api = new api_client_1.ApiClient(options.server);
|
|
16
|
+
const fundingService = new job_funding_service_1.JobFundingService();
|
|
17
|
+
const requesterConfig = workspace.readRequesterConfig();
|
|
18
|
+
const wallets = workspace.readWallets(requesterConfig.authority.path);
|
|
19
|
+
const requesterWallet = (0, authority_util_1.resolveActorWallet)(wallets, requesterConfig.authority.actor);
|
|
20
|
+
const input = JSON.parse(options.input);
|
|
21
|
+
const quote = await api.post('/quote', {
|
|
22
|
+
serviceId: options.service,
|
|
23
|
+
input,
|
|
24
|
+
requesterAddress: requesterWallet.address,
|
|
25
|
+
});
|
|
26
|
+
const requirement = {
|
|
27
|
+
x402Version: 2,
|
|
28
|
+
scheme: 'exact',
|
|
29
|
+
quoteId: quote.quoteId,
|
|
30
|
+
accepts: quote.accepts,
|
|
31
|
+
expiresAt: quote.expiresAt,
|
|
32
|
+
resource: '/jobs',
|
|
33
|
+
description: `Halot escrow payment for ${quote.serviceId}`,
|
|
34
|
+
};
|
|
35
|
+
const authorization = await (0, sdk_1.signPaymentRequirement)(requesterWallet, requirement, options.network);
|
|
36
|
+
const response = await api.postRaw('/jobs/prepare', {
|
|
37
|
+
quoteId: quote.quoteId,
|
|
38
|
+
settlementAddress: (0, authority_util_1.resolveAuthorityAddress)(wallets, sdk_1.SettlementNetworkSchema.parse(authorization.network)),
|
|
39
|
+
}, {
|
|
40
|
+
'PAYMENT-SIGNATURE': encodePaymentSignature(authorization),
|
|
41
|
+
});
|
|
42
|
+
const preparedJob = await response.json();
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(preparedJob?.error ?? 'Failed to prepare job');
|
|
45
|
+
}
|
|
46
|
+
const transactionHash = await fundingService.fundPreparedJob(preparedJob, {
|
|
47
|
+
wallets,
|
|
48
|
+
});
|
|
49
|
+
const body = await api.post('/jobs', {
|
|
50
|
+
prepareId: preparedJob.prepareId,
|
|
51
|
+
transactionHash,
|
|
52
|
+
});
|
|
53
|
+
(0, print_util_1.printJson)({
|
|
54
|
+
requesterId: requesterConfig.requesterId,
|
|
55
|
+
...body,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JobFundingService = void 0;
|
|
4
|
+
const stellar_sdk_1 = require("@stellar/stellar-sdk");
|
|
5
|
+
const ethers_1 = require("ethers");
|
|
6
|
+
const env_1 = require("@halot/cli/shared/config/env");
|
|
7
|
+
const authority_util_1 = require("@halot/cli/shared/wallet/authority.util");
|
|
8
|
+
const erc20Abi = [
|
|
9
|
+
'function allowance(address owner, address spender) view returns (uint256)',
|
|
10
|
+
'function approve(address spender, uint256 amount) returns (bool)',
|
|
11
|
+
];
|
|
12
|
+
const zeroGEscrowAbi = [
|
|
13
|
+
'function createJob(address token, bytes32 jobHash, address provider, address[] verifierAddresses, uint256 threshold, uint256 providerAmount, uint256 verifierPool, uint256 platformFee)',
|
|
14
|
+
];
|
|
15
|
+
class JobFundingService {
|
|
16
|
+
env = (0, env_1.loadCliEnv)();
|
|
17
|
+
async fundPreparedJob(preparedJob, options) {
|
|
18
|
+
if (preparedJob.paymentNetwork.startsWith('0g:')) {
|
|
19
|
+
const authority = (0, authority_util_1.resolveAuthorityWallet)(options.wallets, preparedJob.paymentNetwork);
|
|
20
|
+
const chain = (0, authority_util_1.resolveZeroGAuthorityProfile)(preparedJob.paymentNetwork);
|
|
21
|
+
return this.fundZeroGPreparedJob(preparedJob, authority.privateKey, chain.rpcUrl);
|
|
22
|
+
}
|
|
23
|
+
if (preparedJob.paymentNetwork.startsWith('stellar:')) {
|
|
24
|
+
const authority = (0, authority_util_1.resolveAuthorityWallet)(options.wallets, preparedJob.paymentNetwork);
|
|
25
|
+
return this.fundStellarPreparedJob(preparedJob, authority.privateKey);
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Unsupported settlement network ${preparedJob.paymentNetwork}`);
|
|
28
|
+
}
|
|
29
|
+
async fundZeroGPreparedJob(preparedJob, privateKey, rpcUrl) {
|
|
30
|
+
const provider = new ethers_1.JsonRpcProvider(rpcUrl);
|
|
31
|
+
const wallet = new ethers_1.Wallet(privateKey, provider);
|
|
32
|
+
const token = new ethers_1.Contract(preparedJob.settlementTokenAddress, erc20Abi, wallet);
|
|
33
|
+
const escrow = new ethers_1.Contract(preparedJob.settlementContractAddress, zeroGEscrowAbi, wallet);
|
|
34
|
+
const allowance = await token.allowance(wallet.address, preparedJob.settlementContractAddress);
|
|
35
|
+
const totalAmountUnits = BigInt(preparedJob.totalAmountUnits);
|
|
36
|
+
if (allowance < totalAmountUnits) {
|
|
37
|
+
const approval = await token.approve(preparedJob.settlementContractAddress, ethers_1.MaxUint256);
|
|
38
|
+
await approval.wait();
|
|
39
|
+
}
|
|
40
|
+
const transaction = await escrow.createJob(preparedJob.settlementTokenAddress, preparedJob.jobHash, preparedJob.providerPayoutAddress, preparedJob.assignedVerifiers.map((verifier) => verifier.ownerAddress), preparedJob.threshold, preparedJob.providerAmountUnits, preparedJob.verifierPoolUnits, preparedJob.platformFeeUnits);
|
|
41
|
+
const receipt = await transaction.wait();
|
|
42
|
+
if (!receipt || receipt.status !== 1) {
|
|
43
|
+
throw new Error('0G createJob transaction failed');
|
|
44
|
+
}
|
|
45
|
+
return transaction.hash;
|
|
46
|
+
}
|
|
47
|
+
async fundStellarPreparedJob(preparedJob, secretKey) {
|
|
48
|
+
if (!this.env.defaultStellarRpcUrl || !this.env.defaultStellarNetworkPassphrase) {
|
|
49
|
+
throw new Error('Missing HALOT_DEFAULT_STELLAR_RPC_URL or HALOT_DEFAULT_STELLAR_NETWORK_PASSPHRASE for Stellar funding');
|
|
50
|
+
}
|
|
51
|
+
const server = new stellar_sdk_1.rpc.Server(this.env.defaultStellarRpcUrl);
|
|
52
|
+
const keypair = stellar_sdk_1.Keypair.fromSecret(secretKey);
|
|
53
|
+
const sourceAccount = await server.getAccount(keypair.publicKey());
|
|
54
|
+
const contract = new stellar_sdk_1.Contract(preparedJob.settlementContractAddress);
|
|
55
|
+
const verifierPublicKeys = preparedJob.assignedVerifiers.map((verifier) => stellar_sdk_1.xdr.ScVal.scvBytes(hexToBuffer(verifier.signingPublicKey, 65)));
|
|
56
|
+
const verifierPayouts = preparedJob.assignedVerifiers.map((verifier) => new stellar_sdk_1.Address(verifier.payoutAddress).toScVal());
|
|
57
|
+
const transaction = new stellar_sdk_1.TransactionBuilder(sourceAccount, {
|
|
58
|
+
fee: stellar_sdk_1.BASE_FEE,
|
|
59
|
+
networkPassphrase: this.env.defaultStellarNetworkPassphrase,
|
|
60
|
+
})
|
|
61
|
+
.addOperation(contract.call('create_job', new stellar_sdk_1.Address(preparedJob.settlementTokenAddress).toScVal(), stellar_sdk_1.xdr.ScVal.scvBytes(hexToBuffer(preparedJob.jobHash, 32)), new stellar_sdk_1.Address(preparedJob.requesterAddress).toScVal(), new stellar_sdk_1.Address(preparedJob.providerPayoutAddress).toScVal(), stellar_sdk_1.xdr.ScVal.scvVec(verifierPublicKeys), stellar_sdk_1.xdr.ScVal.scvVec(verifierPayouts), stellar_sdk_1.xdr.ScVal.scvU32(preparedJob.threshold), (0, stellar_sdk_1.nativeToScVal)(BigInt(preparedJob.providerAmountUnits), { type: 'i128' }), (0, stellar_sdk_1.nativeToScVal)(BigInt(preparedJob.verifierPoolUnits), { type: 'i128' }), (0, stellar_sdk_1.nativeToScVal)(BigInt(preparedJob.platformFeeUnits), { type: 'i128' })))
|
|
62
|
+
.setTimeout(30)
|
|
63
|
+
.build();
|
|
64
|
+
const preparedTransaction = await server.prepareTransaction(transaction);
|
|
65
|
+
preparedTransaction.sign(keypair);
|
|
66
|
+
const response = await server.sendTransaction(preparedTransaction);
|
|
67
|
+
if (response.status !== 'PENDING' && response.status !== 'DUPLICATE') {
|
|
68
|
+
throw new Error(`Stellar create_job submission failed with status ${response.status}`);
|
|
69
|
+
}
|
|
70
|
+
await waitForStellarSuccess(server, response.hash);
|
|
71
|
+
return response.hash;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.JobFundingService = JobFundingService;
|
|
75
|
+
function hexToBuffer(value, size) {
|
|
76
|
+
const normalized = value.startsWith('0x') ? value.slice(2) : value;
|
|
77
|
+
if (normalized.length !== size * 2) {
|
|
78
|
+
throw new Error(`Expected ${size} byte hex value, received ${value}`);
|
|
79
|
+
}
|
|
80
|
+
return Buffer.from(normalized, 'hex');
|
|
81
|
+
}
|
|
82
|
+
async function waitForStellarSuccess(server, transactionHash) {
|
|
83
|
+
const deadline = Date.now() + 120_000;
|
|
84
|
+
while (Date.now() <= deadline) {
|
|
85
|
+
const receipt = await server.getTransaction(transactionHash);
|
|
86
|
+
if (receipt.status === stellar_sdk_1.rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (receipt.status === stellar_sdk_1.rpc.Api.GetTransactionStatus.FAILED) {
|
|
90
|
+
throw new Error(`Stellar create_job transaction ${transactionHash} failed`);
|
|
91
|
+
}
|
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
93
|
+
}
|
|
94
|
+
throw new Error(`Timed out waiting for Stellar create_job transaction ${transactionHash}`);
|
|
95
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runJobResultCommand = runJobResultCommand;
|
|
4
|
+
const api_client_1 = require("@halot/cli/shared/http/api-client");
|
|
5
|
+
const print_util_1 = require("@halot/cli/shared/output/print.util");
|
|
6
|
+
async function runJobResultCommand(jobId, options) {
|
|
7
|
+
const api = new api_client_1.ApiClient(options.server);
|
|
8
|
+
const result = await api.get(`/jobs/${jobId}/result`);
|
|
9
|
+
(0, print_util_1.printJson)(result);
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runJobWatchCommand = runJobWatchCommand;
|
|
4
|
+
const api_client_1 = require("@halot/cli/shared/http/api-client");
|
|
5
|
+
const print_util_1 = require("@halot/cli/shared/output/print.util");
|
|
6
|
+
const terminalStatuses = new Set(['approved', 'rejected', 'escalated', 'settlement-failed']);
|
|
7
|
+
async function runJobWatchCommand(jobId, options) {
|
|
8
|
+
const api = new api_client_1.ApiClient(options.server);
|
|
9
|
+
const intervalMs = Number(options.interval);
|
|
10
|
+
do {
|
|
11
|
+
const job = await api.get(`/jobs/${jobId}`);
|
|
12
|
+
(0, print_util_1.printJson)(job);
|
|
13
|
+
if (terminalStatuses.has(job.status)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
await (0, print_util_1.sleep)(intervalMs);
|
|
17
|
+
} while (true);
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runProviderInitCommand = runProviderInitCommand;
|
|
4
|
+
const wallet_manager_1 = require("@halot/cli/shared/wallet/wallet.manager");
|
|
5
|
+
const authority_util_1 = require("@halot/cli/shared/wallet/authority.util");
|
|
6
|
+
const workspace_service_1 = require("@halot/cli/shared/workspace/workspace.service");
|
|
7
|
+
async function runProviderInitCommand() {
|
|
8
|
+
const workspace = new workspace_service_1.WorkspaceService();
|
|
9
|
+
const walletManager = new wallet_manager_1.WalletManager(workspace);
|
|
10
|
+
const wallets = walletManager.createWallets();
|
|
11
|
+
workspace.writeProviderConfig({
|
|
12
|
+
providerId: '',
|
|
13
|
+
displayName: 'My Provider',
|
|
14
|
+
description: 'Provider description',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
identity: {
|
|
17
|
+
erc8004TokenId: '',
|
|
18
|
+
agentRegistry: '',
|
|
19
|
+
agentUri: '',
|
|
20
|
+
ownerAddress: (0, authority_util_1.resolveAuthorityAddress)(wallets, '0g:testnet'),
|
|
21
|
+
domain: 'myprovider.0g',
|
|
22
|
+
domainType: 'space-id',
|
|
23
|
+
},
|
|
24
|
+
authority: {
|
|
25
|
+
path: './wallets.json',
|
|
26
|
+
actor: '0g:testnet',
|
|
27
|
+
},
|
|
28
|
+
settlementWallets: (0, authority_util_1.createDefaultSettlementWallets)(wallets),
|
|
29
|
+
});
|
|
30
|
+
console.log('Provider workspace initialized.');
|
|
31
|
+
console.log(`0G testnet authority: ${(0, authority_util_1.resolveAuthorityAddress)(wallets, '0g:testnet')}`);
|
|
32
|
+
console.log(`Stellar testnet authority: ${(0, authority_util_1.resolveAuthorityAddress)(wallets, 'stellar:testnet')}`);
|
|
33
|
+
}
|