nebula-treasury 0.1.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nebula-treasury",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "nebula CLI: a Mantle-native, policy-aware AI treasury assistant. Real on-chain work gated by policy, simulation, and approval",
6
6
  "license": "MIT",
@@ -49,11 +49,11 @@
49
49
  "@clack/prompts": "^0.8.2",
50
50
  "@opentui/core": "^0.1.97",
51
51
  "@opentui/solid": "^0.1.97",
52
- "nebula-ai-core": "0.1.0",
53
- "nebula-ai-gateway": "0.1.0",
54
- "nebula-ai-plugin-onchain": "0.1.0",
55
- "nebula-ai-plugin-system": "0.1.0",
56
- "nebula-ai-plugin-telegram": "0.1.0",
52
+ "nebula-ai-core": "0.2.0",
53
+ "nebula-ai-gateway": "0.2.0",
54
+ "nebula-ai-plugin-onchain": "0.2.0",
55
+ "nebula-ai-plugin-system": "0.2.0",
56
+ "nebula-ai-plugin-telegram": "0.2.0",
57
57
  "picocolors": "^1.1.1",
58
58
  "qrcode-terminal": "^0.12.0",
59
59
  "solid-js": "^1.9.12",
@@ -0,0 +1,196 @@
1
+ /**
2
+ * `nebula reputation` + `nebula validation` — ERC-8004 Reputation + Validation.
3
+ *
4
+ * nebula reputation show [<agentId>]
5
+ * nebula reputation give --agent <id> --score <0-100> [--tag t] [--uri u]
6
+ * nebula validation show <requestId>
7
+ * nebula validation request --agent <id> --data <str> [--uri u]
8
+ * nebula validation respond --id <reqId> --passed <true|false> [--score n] [--uri u]
9
+ */
10
+ import { cancel, intro, outro, spinner } from '@clack/prompts'
11
+ import {
12
+ NETWORK_RPC,
13
+ agentIdByAddress,
14
+ explorerTxUrl,
15
+ getReputation,
16
+ getValidation,
17
+ giveFeedback,
18
+ requestValidation,
19
+ resolveRegistryAddress,
20
+ resolveReputationRegistry,
21
+ resolveValidationRegistry,
22
+ respondValidation,
23
+ } from 'nebula-ai-core'
24
+ import { http, type Address, createPublicClient, keccak256, toHex } from 'viem'
25
+ import { findAndLoadConfig } from '../config/load'
26
+ import { loadOrPickOperatorSigner } from './init/operator-picker'
27
+
28
+ function flag(argv: string[], name: string): string | undefined {
29
+ const i = argv.indexOf(name)
30
+ return i >= 0 ? argv[i + 1] : undefined
31
+ }
32
+
33
+ export interface TrustArgs {
34
+ kind: 'reputation' | 'validation'
35
+ sub: string
36
+ argv: string[]
37
+ }
38
+
39
+ export function parseTrustArgs(
40
+ kind: 'reputation' | 'validation',
41
+ argv: string[],
42
+ ): TrustArgs | { error: string } {
43
+ const sub = argv[0] ?? ''
44
+ const valid = kind === 'reputation' ? ['show', 'give'] : ['show', 'request', 'respond']
45
+ if (!valid.includes(sub))
46
+ return { error: `unknown subcommand '${sub || '(none)'}' — try: ${valid.join(' | ')}` }
47
+ return { kind, sub, argv: argv.slice(1) }
48
+ }
49
+
50
+ export async function runTrust(args: TrustArgs): Promise<void> {
51
+ const loaded = await findAndLoadConfig()
52
+ if (!loaded) {
53
+ console.error('No nebula.config.ts found. Run `nebula init` first.')
54
+ process.exit(1)
55
+ }
56
+ const { config } = loaded
57
+ const network = config.network
58
+ const publicClient = createPublicClient({ transport: http(NETWORK_RPC[network]) })
59
+ const repReg = resolveReputationRegistry(network)
60
+ const valReg = resolveValidationRegistry(network)
61
+ const idReg = resolveRegistryAddress(network)
62
+
63
+ // ── reads ──
64
+ if (args.kind === 'reputation' && args.sub === 'show') {
65
+ if (!repReg) return fail(`No Reputation Registry for ${network}.`)
66
+ let id = flag(args.argv, '--agent') ?? args.argv.find(a => !a.startsWith('--'))
67
+ if (!id) {
68
+ if (!idReg || !config.identity.agent) return fail('Pass an <agentId>.')
69
+ const resolved = await agentIdByAddress({
70
+ publicClient,
71
+ registry: idReg,
72
+ agentAddress: config.identity.agent as Address,
73
+ })
74
+ if (resolved === 0n)
75
+ return void console.log('this agent is not registered; run `nebula identity register`')
76
+ id = resolved.toString()
77
+ }
78
+ const { count, averageScore } = await getReputation({
79
+ publicClient,
80
+ registry: repReg,
81
+ agentId: BigInt(id),
82
+ })
83
+ console.log(`agent id ${id}`)
84
+ console.log(`ratings ${count}`)
85
+ console.log(`avg score ${averageScore} / 100`)
86
+ return
87
+ }
88
+ if (args.kind === 'validation' && args.sub === 'show') {
89
+ if (!valReg) return fail(`No Validation Registry for ${network}.`)
90
+ const reqId = flag(args.argv, '--id') ?? args.argv.find(a => !a.startsWith('--'))
91
+ if (!reqId) return fail('Pass a <requestId>.')
92
+ const v = await getValidation({ publicClient, registry: valReg, requestId: BigInt(reqId) })
93
+ console.log(`request id ${reqId}`)
94
+ console.log(`agent id ${v.agentId.toString()}`)
95
+ console.log(`requester ${v.requester}`)
96
+ console.log(`status ${v.responded ? (v.passed ? 'PASSED' : 'FAILED') : 'pending'}`)
97
+ if (v.responded) {
98
+ console.log(`validator ${v.validator}`)
99
+ console.log(`score ${v.score} / 100`)
100
+ }
101
+ return
102
+ }
103
+
104
+ // ── writes (need the operator wallet) ──
105
+ intro(`nebula ${args.kind} ${args.sub}`)
106
+ const operator = await loadOrPickOperatorSigner({ network, hint: config.operator })
107
+ if (!operator) return cancel('No operator wallet available.')
108
+ const walletClient = await operator.walletClient(network)
109
+ const s = spinner()
110
+ try {
111
+ if (args.kind === 'reputation' && args.sub === 'give') {
112
+ if (!repReg) throw new Error(`No Reputation Registry for ${network}.`)
113
+ const agentId = BigInt(
114
+ flag(args.argv, '--agent') ??
115
+ (() => {
116
+ throw new Error('--agent <id> required')
117
+ })(),
118
+ )
119
+ const score = Number(
120
+ flag(args.argv, '--score') ??
121
+ (() => {
122
+ throw new Error('--score <0-100> required')
123
+ })(),
124
+ )
125
+ s.start('Recording reputation feedback on-chain')
126
+ const { txHash } = await giveFeedback({
127
+ walletClient,
128
+ publicClient,
129
+ registry: repReg,
130
+ agentId,
131
+ score,
132
+ tag: flag(args.argv, '--tag') ?? '',
133
+ uri: flag(args.argv, '--uri') ?? '',
134
+ })
135
+ s.stop('feedback recorded')
136
+ outro(`tx ${explorerTxUrl(network, txHash)}`)
137
+ } else if (args.kind === 'validation' && args.sub === 'request') {
138
+ if (!valReg) throw new Error(`No Validation Registry for ${network}.`)
139
+ const agentId = BigInt(
140
+ flag(args.argv, '--agent') ??
141
+ (() => {
142
+ throw new Error('--agent <id> required')
143
+ })(),
144
+ )
145
+ const data =
146
+ flag(args.argv, '--data') ??
147
+ (() => {
148
+ throw new Error('--data <string|0xhash> required')
149
+ })()
150
+ const dataHash = /^0x[0-9a-fA-F]{64}$/.test(data)
151
+ ? (data as `0x${string}`)
152
+ : keccak256(toHex(data))
153
+ s.start('Opening validation request on-chain')
154
+ const { requestId, txHash } = await requestValidation({
155
+ walletClient,
156
+ publicClient,
157
+ registry: valReg,
158
+ agentId,
159
+ dataHash,
160
+ uri: flag(args.argv, '--uri') ?? '',
161
+ })
162
+ s.stop(`request id ${requestId.toString()}`)
163
+ outro(`tx ${explorerTxUrl(network, txHash)}`)
164
+ } else if (args.kind === 'validation' && args.sub === 'respond') {
165
+ if (!valReg) throw new Error(`No Validation Registry for ${network}.`)
166
+ const requestId = BigInt(
167
+ flag(args.argv, '--id') ??
168
+ (() => {
169
+ throw new Error('--id <requestId> required')
170
+ })(),
171
+ )
172
+ const passed = (flag(args.argv, '--passed') ?? 'true') !== 'false'
173
+ s.start('Publishing validation response on-chain')
174
+ const { txHash } = await respondValidation({
175
+ walletClient,
176
+ publicClient,
177
+ registry: valReg,
178
+ requestId,
179
+ passed,
180
+ score: Number(flag(args.argv, '--score') ?? '0'),
181
+ uri: flag(args.argv, '--uri') ?? '',
182
+ })
183
+ s.stop('response published')
184
+ outro(`tx ${explorerTxUrl(network, txHash)}`)
185
+ }
186
+ } catch (e) {
187
+ s.stop(`failed: ${(e as Error).message.slice(0, 200)}`)
188
+ } finally {
189
+ await operator.close?.()
190
+ }
191
+ }
192
+
193
+ function fail(msg: string): void {
194
+ console.error(msg)
195
+ process.exit(1)
196
+ }
package/src/index.ts CHANGED
@@ -61,6 +61,17 @@ async function main(): Promise<void> {
61
61
  await runIdentity(parsed)
62
62
  return
63
63
  }
64
+ case 'reputation':
65
+ case 'validation': {
66
+ const { parseTrustArgs, runTrust } = await import('./commands/trust')
67
+ const parsed = parseTrustArgs(sub, argv.slice(1))
68
+ if ('error' in parsed) {
69
+ console.error(`nebula ${sub}: ${parsed.error}`)
70
+ process.exit(1)
71
+ }
72
+ await runTrust(parsed)
73
+ return
74
+ }
64
75
  case 'telegram': {
65
76
  const { parseTelegramArgs, runTelegram } = await import('./commands/telegram')
66
77
  const parsed = parseTelegramArgs(argv.slice(1))
@@ -126,6 +137,8 @@ function printHelp(): void {
126
137
  ' nebula drain --to <addr> sweep agent EOA balance to address (default: operator)',
127
138
  ' nebula model re-pick the brain model',
128
139
  ' nebula identity <sub> ERC-8004 agent identity (subs: card | register | show)',
140
+ ' nebula reputation <sub> ERC-8004 reputation (subs: show | give)',
141
+ ' nebula validation <sub> ERC-8004 validation (subs: show | request | respond)',
129
142
  ' nebula telegram <sub> configure phone-DM gateway (subs: setup | status | remove)',
130
143
  ' nebula pairing <sub> manage DM pairing approvals (subs: list | approve | revoke | clear-pending)',
131
144
  ' usage: nebula pairing approve telegram <code>',