opmsec 0.1.3 → 0.1.5
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 +1 -0
- package/.husky/pre-commit +1 -0
- package/README.md +71 -275
- package/bun.lock +5 -5
- package/docs/architecture/agents.mdx +11 -59
- package/docs/architecture/benchmarks.mdx +20 -46
- package/docs/architecture/overview.mdx +31 -38
- package/docs/architecture/scanner.mdx +11 -37
- package/docs/cli/audit.mdx +9 -12
- package/docs/cli/check.mdx +12 -26
- package/docs/cli/fix.mdx +10 -30
- package/docs/cli/info.mdx +12 -19
- package/docs/cli/install.mdx +27 -39
- package/docs/cli/push.mdx +40 -57
- package/docs/cli/register-agent.mdx +21 -53
- package/docs/cli/view.mdx +12 -29
- package/docs/concepts/ens-records.mdx +44 -0
- package/docs/concepts/multi-agent-consensus.mdx +18 -36
- package/docs/concepts/on-chain-registry.mdx +22 -49
- package/docs/concepts/security-model.mdx +20 -52
- package/docs/concepts/zk-agent-verification.mdx +26 -64
- package/docs/contract/events.mdx +13 -74
- package/docs/contract/functions.mdx +40 -126
- package/docs/contract/overview.mdx +17 -36
- package/docs/introduction.mdx +22 -25
- package/docs/mint.json +3 -2
- package/docs/quickstart.mdx +34 -70
- package/docs/system-design.png +0 -0
- package/package.json +7 -6
- package/packages/cli/src/commands/author-view.tsx +87 -2
- package/packages/cli/src/commands/check.tsx +18 -5
- package/packages/cli/src/commands/fix.tsx +25 -12
- package/packages/cli/src/commands/info.tsx +92 -4
- package/packages/cli/src/commands/install.tsx +327 -23
- package/packages/cli/src/commands/push.tsx +112 -0
- package/packages/cli/src/commands/register-agent.tsx +72 -31
- package/packages/cli/src/index.tsx +7 -5
- package/packages/cli/src/services/ens-records.ts +525 -0
- package/packages/cli/src/services/version.ts +156 -5
- package/packages/core/src/benchmarks.ts +116 -0
- package/packages/core/src/constants.ts +18 -6
- package/packages/core/src/model-rankings.ts +40 -15
- package/packages/core/src/types.ts +10 -0
- package/packages/core/src/utils.ts +136 -1
- package/packages/scanner/src/index.ts +2 -1
- package/packages/scanner/src/queue/memory-queue.ts +7 -2
- package/packages/scanner/src/services/benchmark-runner.ts +86 -1
- package/packages/scanner/src/services/fileverse.ts +61 -12
- package/packages/scanner/src/services/openrouter.ts +18 -7
- package/packages/web/.next/BUILD_ID +1 -0
- package/packages/web/.next/app-path-routes-manifest.json +4 -0
- package/packages/web/.next/diagnostics/build-diagnostics.json +6 -0
- package/packages/web/.next/diagnostics/framework.json +1 -0
- package/packages/web/.next/export-marker.json +6 -0
- package/packages/web/.next/images-manifest.json +58 -0
- package/packages/web/.next/next-minimal-server.js.nft.json +1 -0
- package/packages/web/.next/next-server.js.nft.json +1 -0
- package/packages/web/.next/prerender-manifest.json +54 -4
- package/packages/web/.next/required-server-files.json +320 -0
- package/packages/web/.next/routes-manifest.json +53 -1
- package/packages/web/.next/server/app/_not-found/page.js +2 -0
- package/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/packages/web/.next/server/app/_not-found.html +1 -0
- package/packages/web/.next/server/app/_not-found.meta +8 -0
- package/packages/web/.next/server/app/_not-found.rsc +18 -0
- package/packages/web/.next/server/app/index.html +6 -0
- package/packages/web/.next/server/app/index.meta +7 -0
- package/packages/web/.next/server/app/index.rsc +22 -0
- package/packages/web/.next/server/app/page.js +24 -24
- package/packages/web/.next/server/app/page.js.nft.json +1 -0
- package/packages/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/packages/web/.next/server/chunks/611.js +6 -0
- package/packages/web/.next/server/chunks/778.js +30 -0
- package/packages/web/.next/server/functions-config-manifest.json +4 -0
- package/packages/web/.next/server/interception-route-rewrite-manifest.js +1 -1
- package/packages/web/.next/server/next-font-manifest.js +1 -1
- package/packages/web/.next/server/next-font-manifest.json +1 -1
- package/packages/web/.next/server/pages/404.html +1 -0
- package/packages/web/.next/server/pages/500.html +1 -0
- package/packages/web/.next/server/pages/_app.js +1 -0
- package/packages/web/.next/server/pages/_app.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_document.js +1 -0
- package/packages/web/.next/server/pages/_document.js.nft.json +1 -0
- package/packages/web/.next/server/pages/_error.js +19 -0
- package/packages/web/.next/server/pages/_error.js.nft.json +1 -0
- package/packages/web/.next/server/webpack-runtime.js +2 -2
- package/packages/web/.next/static/0esGzFBCzREfVwijEGDfL/_buildManifest.js +1 -0
- package/packages/web/.next/static/0esGzFBCzREfVwijEGDfL/_ssgManifest.js +1 -0
- package/packages/web/.next/static/chunks/174-5b5efcb3b8efcc01.js +1 -0
- package/packages/web/.next/static/chunks/255-0dc49b7a6e8e5c05.js +1 -0
- package/packages/web/.next/static/chunks/4bd1b696-382748cc942d8a14.js +1 -0
- package/packages/web/.next/static/chunks/app/_not-found/page-0da542be7eb33a64.js +1 -0
- package/packages/web/.next/static/chunks/app/layout-de8e841104500505.js +1 -0
- package/packages/web/.next/static/chunks/app/layout.js +37 -7
- package/packages/web/.next/static/chunks/app/page-7e086379698b9fb0.js +1 -0
- package/packages/web/.next/static/chunks/app/page.js +297 -1
- package/packages/web/.next/static/chunks/framework-ac73abd125e371fe.js +1 -0
- package/packages/web/.next/static/chunks/main-4e8d71b5ef7ee7e3.js +1 -0
- package/packages/web/.next/static/chunks/main-app-dd261207182e5a23.js +1 -0
- package/packages/web/.next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/packages/web/.next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/packages/web/.next/static/chunks/webpack-0dcd67569eb46132.js +1 -0
- package/packages/web/.next/static/chunks/webpack.js +2 -2
- package/packages/web/.next/static/css/102562cf2d0ae9b0.css +3 -0
- package/packages/web/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- package/packages/web/.next/static/media/747892c23ea88013-s.woff2 +0 -0
- package/packages/web/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/packages/web/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- package/packages/web/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/packages/web/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- package/packages/web/.next/static/webpack/16f18baa938a434c.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/5fe9fe8578f9c3d2.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/73c7d02260cc80e4.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/a2d85d19aa028de1.webpack.hot-update.json +1 -0
- package/packages/web/.next/static/webpack/app/{layout.73e341375c8d429e.hot-update.js → layout.16f18baa938a434c.hot-update.js} +1 -1
- package/packages/web/.next/static/webpack/app/{layout.6fee6306e0f98869.hot-update.js → layout.5fe9fe8578f9c3d2.hot-update.js} +1 -1
- package/packages/web/.next/static/webpack/app/layout.653e365406c0d9ac.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.6800169a899e3a8b.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.73c7d02260cc80e4.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/layout.a2d85d19aa028de1.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.653e365406c0d9ac.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.6800169a899e3a8b.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.73c7d02260cc80e4.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/app/page.a2d85d19aa028de1.hot-update.js +22 -0
- package/packages/web/.next/static/webpack/{webpack.6fee6306e0f98869.hot-update.js → webpack.16f18baa938a434c.hot-update.js} +2 -2
- package/packages/web/.next/static/webpack/{webpack.73e341375c8d429e.hot-update.js → webpack.5fe9fe8578f9c3d2.hot-update.js} +2 -2
- package/packages/web/.next/static/webpack/webpack.653e365406c0d9ac.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.6800169a899e3a8b.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.73c7d02260cc80e4.hot-update.js +12 -0
- package/packages/web/.next/static/webpack/webpack.a2d85d19aa028de1.hot-update.js +12 -0
- package/packages/web/.next/trace +2 -5
- package/packages/web/app/globals.css +197 -51
- package/packages/web/app/layout.tsx +6 -3
- package/packages/web/app/page.tsx +791 -309
- package/packages/web/bun.lock +66 -105
- package/packages/web/next.config.ts +8 -1
- package/packages/web/package.json +5 -2
- package/packages/web/postcss.config.mjs +2 -2
- package/packages/web/public/apple-icon.png +1 -0
- package/packages/web/public/dependency-bottleneck.png +0 -0
- package/packages/web/public/icon-dark-32x32.png +1 -0
- package/packages/web/public/icon-light-32x32.png +1 -0
- package/packages/web/public/icon.svg +1 -0
- package/packages/web/public/nextjs-cve-announcement.png +0 -0
- package/packages/web/public/phantomraven-npm-attack.png +0 -0
- package/packages/web/public/placeholder-logo.png +1 -0
- package/packages/web/public/placeholder-logo.svg +1 -0
- package/packages/web/public/placeholder-user.jpg +1 -0
- package/packages/web/public/placeholder.jpg +1 -0
- package/packages/web/public/placeholder.svg +1 -0
- package/packages/web/public/react-cve-meme.png +0 -0
- package/packages/web/public/wallet-drain-exploit.png +0 -0
- package/packages/web/styles/globals.css +125 -0
- package/packages/web/.next/server/vendor-chunks/@swc.js +0 -55
- package/packages/web/.next/server/vendor-chunks/next.js +0 -3010
- package/packages/web/.next/static/chunks/app-pages-internals.js +0 -182
- package/packages/web/.next/static/chunks/main-app.js +0 -1882
- package/packages/web/.next/static/css/app/layout.css +0 -1237
- package/packages/web/.next/static/webpack/633457081244afec._.hot-update.json +0 -1
- package/packages/web/.next/static/webpack/app/page.6fee6306e0f98869.hot-update.js +0 -22
- package/packages/web/.next/static/webpack/app/page.73e341375c8d429e.hot-update.js +0 -22
- package/packages/web/tailwind.config.ts +0 -48
- /package/packages/web/.next/static/chunks/{polyfills.js → polyfills-42372ed130431b0a.js} +0 -0
- /package/packages/web/.next/static/webpack/{6fee6306e0f98869.webpack.hot-update.json → 653e365406c0d9ac.webpack.hot-update.json} +0 -0
- /package/packages/web/.next/static/webpack/{73e341375c8d429e.webpack.hot-update.json → 6800169a899e3a8b.webpack.hot-update.json} +0 -0
|
@@ -9,6 +9,7 @@ import { Hyperlink } from '../components/Hyperlink';
|
|
|
9
9
|
import { computeChecksum, signChecksumAsync } from '../services/signature';
|
|
10
10
|
import { resolveENSName } from '../services/ens';
|
|
11
11
|
import { registerPackageOnChain } from '../services/contract';
|
|
12
|
+
import { writeENSRecords, buildOPMRecords, readOPMRecords, createPackageSubname, setENSContenthash, parseFileverseLink, readFileverseContentHash } from '../services/ens-records';
|
|
12
13
|
import { enqueueScan } from '@opm/scanner';
|
|
13
14
|
import * as fs from 'fs';
|
|
14
15
|
import * as path from 'path';
|
|
@@ -23,6 +24,7 @@ interface Steps {
|
|
|
23
24
|
scan: StepStatus;
|
|
24
25
|
publish: StepStatus;
|
|
25
26
|
register: StepStatus;
|
|
27
|
+
ensRecords: StepStatus;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
interface PushResult {
|
|
@@ -39,6 +41,11 @@ interface PushResult {
|
|
|
39
41
|
agents?: AgentEntry[];
|
|
40
42
|
blocked?: boolean;
|
|
41
43
|
blockReason?: string;
|
|
44
|
+
ensRecordsTx?: string;
|
|
45
|
+
ensRecordsChain?: string;
|
|
46
|
+
ensRecordsCount?: number;
|
|
47
|
+
ensSubname?: string;
|
|
48
|
+
ipfsContenthash?: string;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
interface PushCommandProps {
|
|
@@ -50,10 +57,12 @@ export function PushCommand({ npmToken, otp }: PushCommandProps) {
|
|
|
50
57
|
const [steps, setSteps] = useState<Steps>({
|
|
51
58
|
pack: 'pending', sign: 'pending', ens: 'pending',
|
|
52
59
|
scan: 'pending', publish: 'pending', register: 'pending',
|
|
60
|
+
ensRecords: 'pending',
|
|
53
61
|
});
|
|
54
62
|
const [result, setResult] = useState<PushResult>({});
|
|
55
63
|
const [error, setError] = useState<string | null>(null);
|
|
56
64
|
const [scanLogs, setScanLogs] = useState<string[]>([]);
|
|
65
|
+
const [ensRecordLogs, setEnsRecordLogs] = useState<string[]>([]);
|
|
57
66
|
const [pkgLabel, setPkgLabel] = useState('');
|
|
58
67
|
|
|
59
68
|
const updateStep = (key: keyof Steps, status: StepStatus) =>
|
|
@@ -93,6 +102,9 @@ export function PushCommand({ npmToken, otp }: PushCommandProps) {
|
|
|
93
102
|
|
|
94
103
|
updateStep('scan', 'running');
|
|
95
104
|
let scanPassed = false;
|
|
105
|
+
let finalReportURI: string | undefined;
|
|
106
|
+
let finalRiskScore: number | undefined;
|
|
107
|
+
let finalIpfsHash: string | undefined;
|
|
96
108
|
try {
|
|
97
109
|
const scanResult = await enqueueScan(name, version, (msg) =>
|
|
98
110
|
setScanLogs((prev) => [...prev.slice(-8), msg]),
|
|
@@ -101,6 +113,9 @@ export function PushCommand({ npmToken, otp }: PushCommandProps) {
|
|
|
101
113
|
|
|
102
114
|
const riskScore = scanResult.report.aggregate_risk_score;
|
|
103
115
|
const riskLevel = classifyRisk(riskScore);
|
|
116
|
+
finalReportURI = scanResult.reportURI;
|
|
117
|
+
finalRiskScore = riskScore;
|
|
118
|
+
finalIpfsHash = scanResult.ipfsHash;
|
|
104
119
|
|
|
105
120
|
setResult((r) => ({
|
|
106
121
|
...r,
|
|
@@ -206,6 +221,74 @@ export function PushCommand({ npmToken, otp }: PushCommandProps) {
|
|
|
206
221
|
}
|
|
207
222
|
updateStep('register', 'done');
|
|
208
223
|
|
|
224
|
+
// ── Write package metadata to ENS text records ──
|
|
225
|
+
if (ensName) {
|
|
226
|
+
updateStep('ensRecords', 'running');
|
|
227
|
+
const ensLog = (msg: string) => setEnsRecordLogs((prev) => [...prev, msg]);
|
|
228
|
+
try {
|
|
229
|
+
ensLog(`Reading existing records from ${ensName}...`);
|
|
230
|
+
const existingRecords = await readOPMRecords(ensName);
|
|
231
|
+
const records = buildOPMRecords({
|
|
232
|
+
packageName: name,
|
|
233
|
+
version,
|
|
234
|
+
checksum,
|
|
235
|
+
signature,
|
|
236
|
+
reportURI: finalReportURI,
|
|
237
|
+
riskScore: finalRiskScore,
|
|
238
|
+
existingPackages: existingRecords.packages,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const writeResult = await writeENSRecords(
|
|
242
|
+
ensName,
|
|
243
|
+
privateKey,
|
|
244
|
+
records,
|
|
245
|
+
ensLog,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (writeResult) {
|
|
249
|
+
setResult((r) => ({
|
|
250
|
+
...r,
|
|
251
|
+
ensRecordsTx: writeResult.txHash,
|
|
252
|
+
ensRecordsChain: writeResult.chain,
|
|
253
|
+
ensRecordsCount: writeResult.recordCount,
|
|
254
|
+
}));
|
|
255
|
+
} else {
|
|
256
|
+
ensLog(`Hint: signer needs ETH on Ethereum (Sepolia or Mainnet) for gas, and must be the manager of ${ensName}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let ipfsCid = finalIpfsHash;
|
|
260
|
+
if (!ipfsCid && finalReportURI) {
|
|
261
|
+
const fvLink = parseFileverseLink(finalReportURI);
|
|
262
|
+
if (fvLink) {
|
|
263
|
+
ensLog(`Reading IPFS hash from Fileverse contract ${fvLink.portalAddress.slice(0, 10)}... file #${fvLink.fileId}`);
|
|
264
|
+
ipfsCid = (await readFileverseContentHash(fvLink.portalAddress, fvLink.fileId, ensLog)) ?? undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (ipfsCid) {
|
|
268
|
+
const chResult = await setENSContenthash(ensName, privateKey, ipfsCid, ensLog);
|
|
269
|
+
if (chResult) {
|
|
270
|
+
setResult((r) => ({ ...r, ipfsContenthash: ipfsCid }));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const subResult = await createPackageSubname(
|
|
275
|
+
ensName,
|
|
276
|
+
name,
|
|
277
|
+
privateKey,
|
|
278
|
+
records,
|
|
279
|
+
ensLog,
|
|
280
|
+
);
|
|
281
|
+
if (subResult) {
|
|
282
|
+
setResult((r) => ({ ...r, ensSubname: subResult.subname }));
|
|
283
|
+
}
|
|
284
|
+
} catch (err: any) {
|
|
285
|
+
ensLog(`Error: ${err?.message || 'unknown'}`);
|
|
286
|
+
}
|
|
287
|
+
updateStep('ensRecords', 'done');
|
|
288
|
+
} else {
|
|
289
|
+
updateStep('ensRecords', 'skip');
|
|
290
|
+
}
|
|
291
|
+
|
|
209
292
|
if (fs.existsSync(tarballFile)) fs.unlinkSync(tarballFile);
|
|
210
293
|
}
|
|
211
294
|
|
|
@@ -325,6 +408,35 @@ export function PushCommand({ npmToken, otp }: PushCommandProps) {
|
|
|
325
408
|
</Box>
|
|
326
409
|
</Box>
|
|
327
410
|
)}
|
|
411
|
+
<StatusLine label="Write ENS records" status={steps.ensRecords}
|
|
412
|
+
detail={steps.ensRecords === 'skip' ? 'no ENS name' : steps.ensRecords === 'done' && result.ensRecordsCount ? `${result.ensRecordsCount} records` : undefined} />
|
|
413
|
+
{steps.ensRecords === 'done' && result.ensRecordsTx && (
|
|
414
|
+
<Box flexDirection="column" marginLeft={4}>
|
|
415
|
+
<Box>
|
|
416
|
+
<Text color="gray">Chain: </Text>
|
|
417
|
+
<Text color="cyan">{result.ensRecordsChain}</Text>
|
|
418
|
+
<Text color="gray"> | </Text>
|
|
419
|
+
<Hyperlink url={`https://${result.ensRecordsChain === 'sepolia' ? 'sepolia.' : ''}etherscan.io/tx/${result.ensRecordsTx}`} label={`tx ${result.ensRecordsTx.slice(0, 10)}...`} color="green" />
|
|
420
|
+
</Box>
|
|
421
|
+
<Box>
|
|
422
|
+
<Text color="gray">Records: </Text>
|
|
423
|
+
<Text color="white">url, opm.version, opm.checksum, opm.fileverse, opm.risk_score{result.ipfsContenthash ? ', contenthash' : ''}</Text>
|
|
424
|
+
</Box>
|
|
425
|
+
{result.ensSubname && (
|
|
426
|
+
<Box>
|
|
427
|
+
<Text color="gray">Subname: </Text>
|
|
428
|
+
<Text color="cyan" bold>{result.ensSubname}</Text>
|
|
429
|
+
</Box>
|
|
430
|
+
)}
|
|
431
|
+
</Box>
|
|
432
|
+
)}
|
|
433
|
+
{ensRecordLogs.length > 0 && (
|
|
434
|
+
<Box flexDirection="column" marginLeft={4}>
|
|
435
|
+
{ensRecordLogs.map((log, i) => (
|
|
436
|
+
<Text key={i} color={log.startsWith('Hint:') || log.startsWith('Error:') ? 'yellow' : 'gray'}>{log}</Text>
|
|
437
|
+
))}
|
|
438
|
+
</Box>
|
|
439
|
+
)}
|
|
328
440
|
</>
|
|
329
441
|
)}
|
|
330
442
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
+
import { ethers } from 'ethers';
|
|
3
4
|
import { txUrl, contractUrl, addressUrl } from '@opm/core';
|
|
4
5
|
import { Header } from '../components/Header';
|
|
5
6
|
import { StatusLine, type Status } from '../components/StatusLine';
|
|
6
7
|
import { Hyperlink } from '../components/Hyperlink';
|
|
7
8
|
import { registerAgentOnChain } from '../services/contract';
|
|
8
|
-
import {
|
|
9
|
+
import { runBatchBenchmarkSuite, type BatchBenchmarkRunResult } from '@opm/scanner';
|
|
9
10
|
|
|
10
11
|
type StepStatus = Status;
|
|
11
12
|
|
|
@@ -19,9 +20,12 @@ interface Steps {
|
|
|
19
20
|
interface RegisterResult {
|
|
20
21
|
agentName?: string;
|
|
21
22
|
model?: string;
|
|
22
|
-
benchmarkResult?: BenchmarkRunResult;
|
|
23
|
-
txHash?: string;
|
|
24
23
|
agentAddress?: string;
|
|
24
|
+
benchmarkResult?: BatchBenchmarkRunResult;
|
|
25
|
+
txHash?: string;
|
|
26
|
+
onChainProofHash?: string;
|
|
27
|
+
onChainPromptHash?: string;
|
|
28
|
+
alreadyRegistered?: boolean;
|
|
25
29
|
rejected?: boolean;
|
|
26
30
|
rejectReason?: string;
|
|
27
31
|
}
|
|
@@ -69,10 +73,16 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
69
73
|
throw new Error('OPENROUTER_API_KEY or OPENAI_API_KEY required to run benchmarks');
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
// Derive agent wallet address from private key
|
|
77
|
+
const agentWallet = new ethers.Wallet(process.env.AGENT_PRIVATE_KEY);
|
|
78
|
+
setResult((r) => ({ ...r, agentAddress: agentWallet.address }));
|
|
79
|
+
|
|
72
80
|
updateStep('validate', 'done');
|
|
73
81
|
|
|
82
|
+
// Single-call batch benchmark: sends all 10 cases at once,
|
|
83
|
+
// gets back 10 flagged/safe answers, compares to ground truth.
|
|
74
84
|
updateStep('benchmark', 'running');
|
|
75
|
-
const benchResult = await
|
|
85
|
+
const benchResult = await runBatchBenchmarkSuite(
|
|
76
86
|
{ name: agentName, model, systemPrompt },
|
|
77
87
|
(msg) => setLogs((prev) => [...prev.slice(-12), msg]),
|
|
78
88
|
);
|
|
@@ -82,11 +92,15 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
82
92
|
updateStep('benchmark', 'error');
|
|
83
93
|
updateStep('zkproof', 'error');
|
|
84
94
|
updateStep('register', 'blocked');
|
|
95
|
+
const failedCases = benchResult.results
|
|
96
|
+
.filter((r) => !r.passed)
|
|
97
|
+
.map((r) => `${r.caseId} (${r.category}): answered ${r.actualFlagged ? 'FLAGGED' : 'SAFE'}, expected ${r.expectedFlagged ? 'FLAGGED' : 'SAFE'}`);
|
|
85
98
|
setResult((r) => ({
|
|
86
99
|
...r,
|
|
87
100
|
rejected: true,
|
|
88
101
|
rejectReason: `Agent achieved ${benchResult.accuracyPct}% accuracy (100% required). ` +
|
|
89
|
-
`Failed ${benchResult.failed}/${benchResult.total}
|
|
102
|
+
`Failed ${benchResult.failed}/${benchResult.total} cases.\n` +
|
|
103
|
+
failedCases.join('\n'),
|
|
90
104
|
}));
|
|
91
105
|
return;
|
|
92
106
|
}
|
|
@@ -103,13 +117,19 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
103
117
|
}));
|
|
104
118
|
return;
|
|
105
119
|
}
|
|
120
|
+
|
|
121
|
+
// Compute the on-chain hashes (same as registerAgentOnChain does)
|
|
122
|
+
const proofStr = benchResult.zkProof.accuracyProof;
|
|
123
|
+
const promptStr = systemPrompt || 'default-opm-security-prompt';
|
|
124
|
+
const onChainProofHash = ethers.keccak256(ethers.toUtf8Bytes(proofStr));
|
|
125
|
+
const onChainPromptHash = ethers.keccak256(ethers.toUtf8Bytes(promptStr));
|
|
126
|
+
|
|
127
|
+
setResult((r) => ({ ...r, onChainProofHash, onChainPromptHash }));
|
|
106
128
|
setLogs((prev) => [...prev, `ZK proof hash: ${benchResult.zkProof.accuracyProof.slice(0, 24)}…`]);
|
|
107
129
|
updateStep('zkproof', 'done');
|
|
108
130
|
|
|
109
131
|
updateStep('register', 'running');
|
|
110
132
|
try {
|
|
111
|
-
const proofStr = benchResult.zkProof.accuracyProof;
|
|
112
|
-
const promptStr = systemPrompt || 'default-opm-security-prompt';
|
|
113
133
|
const txHash = await registerAgentOnChain(agentName, model, promptStr, proofStr);
|
|
114
134
|
setResult((r) => ({ ...r, txHash }));
|
|
115
135
|
setLogs((prev) => [...prev, `Agent registered on-chain ✓`]);
|
|
@@ -117,25 +137,29 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
117
137
|
const msg = err?.shortMessage || err?.message || 'failed';
|
|
118
138
|
setLogs((prev) => [...prev, `Registration: ${msg}`]);
|
|
119
139
|
if (msg.includes('already')) {
|
|
120
|
-
setResult((r) => ({ ...r,
|
|
121
|
-
updateStep('register', '
|
|
140
|
+
setResult((r) => ({ ...r, alreadyRegistered: true }));
|
|
141
|
+
updateStep('register', 'done');
|
|
122
142
|
return;
|
|
123
143
|
}
|
|
124
144
|
}
|
|
125
145
|
updateStep('register', 'done');
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
const riskColor = (pct: number) => (pct >= 100 ? 'green' : pct >= 70 ? 'yellow' : 'red');
|
|
129
|
-
|
|
130
148
|
return (
|
|
131
149
|
<Box flexDirection="column">
|
|
132
150
|
<Header subtitle="register-agent" />
|
|
133
151
|
<Text color="white" bold> Registering agent: {agentName}</Text>
|
|
134
152
|
<Text color="gray"> Model: {model}</Text>
|
|
153
|
+
{result.agentAddress && (
|
|
154
|
+
<Box>
|
|
155
|
+
<Text color="gray"> Wallet: </Text>
|
|
156
|
+
<Hyperlink url={addressUrl(result.agentAddress)} label={result.agentAddress} color="cyan" />
|
|
157
|
+
</Box>
|
|
158
|
+
)}
|
|
135
159
|
<Text> </Text>
|
|
136
160
|
|
|
137
161
|
<StatusLine label="Validate configuration" status={steps.validate} />
|
|
138
|
-
<StatusLine label="
|
|
162
|
+
<StatusLine label="Batch benchmark (10 cases, single call)" status={steps.benchmark} />
|
|
139
163
|
|
|
140
164
|
{logs.length > 0 && (
|
|
141
165
|
<Box flexDirection="column" marginLeft={4}>
|
|
@@ -148,21 +172,23 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
148
172
|
{result.benchmarkResult && (
|
|
149
173
|
<Box flexDirection="column" marginTop={1}>
|
|
150
174
|
<Text color="gray">────────────────────────────────────────</Text>
|
|
151
|
-
<Text color="white" bold> Benchmark Results</Text>
|
|
175
|
+
<Text color="white" bold> Benchmark Results (flagged/safe)</Text>
|
|
152
176
|
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
|
153
|
-
{result.benchmarkResult.results.map((r) => (
|
|
177
|
+
{result.benchmarkResult.results.map((r, i) => (
|
|
154
178
|
<Box key={r.caseId}>
|
|
155
|
-
<Text color={r.
|
|
156
|
-
{r.
|
|
179
|
+
<Text color={r.passed ? 'green' : 'red'}>
|
|
180
|
+
{r.passed ? '✓' : '✗'}{' '}
|
|
157
181
|
</Text>
|
|
158
|
-
<Text color="white">{r.category}</Text>
|
|
159
|
-
<Text color="gray"> —
|
|
160
|
-
|
|
182
|
+
<Text color="white">Case {i + 1} ({r.category})</Text>
|
|
183
|
+
<Text color="gray"> — {r.actualFlagged ? 'FLAGGED' : 'SAFE'}</Text>
|
|
184
|
+
{!r.passed && (
|
|
185
|
+
<Text color="red"> (expected {r.expectedFlagged ? 'FLAGGED' : 'SAFE'})</Text>
|
|
186
|
+
)}
|
|
161
187
|
</Box>
|
|
162
188
|
))}
|
|
163
189
|
</Box>
|
|
164
190
|
<Box marginLeft={2} marginTop={1}>
|
|
165
|
-
<Text color={
|
|
191
|
+
<Text color={result.benchmarkResult.accuracyPct >= 100 ? 'green' : 'red'} bold>
|
|
166
192
|
Accuracy: {result.benchmarkResult.passed}/{result.benchmarkResult.total}{' '}
|
|
167
193
|
({result.benchmarkResult.accuracyPct}%)
|
|
168
194
|
</Text>
|
|
@@ -174,8 +200,14 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
174
200
|
<StatusLine label="ZK proof verification" status={steps.zkproof} />
|
|
175
201
|
{result.benchmarkResult?.zkProof && steps.zkproof === 'done' && (
|
|
176
202
|
<Box flexDirection="column" marginLeft={4}>
|
|
177
|
-
<Text color="gray">Commitment:
|
|
178
|
-
<Text color="gray">Proof:
|
|
203
|
+
<Text color="gray">Commitment: {result.benchmarkResult.zkProof.commitment.expectedHash.slice(0, 24)}…</Text>
|
|
204
|
+
<Text color="gray">Proof: {result.benchmarkResult.zkProof.accuracyProof.slice(0, 24)}…</Text>
|
|
205
|
+
{result.onChainProofHash && (
|
|
206
|
+
<Text color="gray">On-chain proof: {result.onChainProofHash.slice(0, 24)}…</Text>
|
|
207
|
+
)}
|
|
208
|
+
{result.onChainPromptHash && (
|
|
209
|
+
<Text color="gray">Prompt hash: {result.onChainPromptHash.slice(0, 24)}…</Text>
|
|
210
|
+
)}
|
|
179
211
|
<Text color="green">✓ Zero-knowledge proof verified — accuracy proven without revealing test data</Text>
|
|
180
212
|
</Box>
|
|
181
213
|
)}
|
|
@@ -187,6 +219,9 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
187
219
|
<Text color="gray">⛓ </Text>
|
|
188
220
|
<Hyperlink url={txUrl(result.txHash)} label={`tx ${result.txHash.slice(0, 10)}…`} color="green" />
|
|
189
221
|
</Box>
|
|
222
|
+
{result.onChainProofHash && (
|
|
223
|
+
<Text color="gray"> ZK proof stored: {result.onChainProofHash.slice(0, 18)}…</Text>
|
|
224
|
+
)}
|
|
190
225
|
<Box>
|
|
191
226
|
<Text color="gray">📋 </Text>
|
|
192
227
|
<Hyperlink url={contractUrl()} label="OPM Registry Contract" color="cyan" />
|
|
@@ -194,6 +229,20 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
194
229
|
</Box>
|
|
195
230
|
)}
|
|
196
231
|
|
|
232
|
+
{result.alreadyRegistered && !result.rejected && (
|
|
233
|
+
<Box flexDirection="column" marginLeft={4}>
|
|
234
|
+
<Text color="yellow">⚠ Agent wallet is already registered on-chain</Text>
|
|
235
|
+
{result.agentAddress && (
|
|
236
|
+
<Box>
|
|
237
|
+
<Text color="gray"> Agent: </Text>
|
|
238
|
+
<Hyperlink url={addressUrl(result.agentAddress)} label={result.agentAddress.slice(0, 10) + '…'} color="cyan" />
|
|
239
|
+
</Box>
|
|
240
|
+
)}
|
|
241
|
+
<Text color="gray"> Benchmark passed ✓ — ZK proof valid ✓</Text>
|
|
242
|
+
<Text color="gray"> Use a different AGENT_PRIVATE_KEY to register a new agent.</Text>
|
|
243
|
+
</Box>
|
|
244
|
+
)}
|
|
245
|
+
|
|
197
246
|
{result.rejected && (
|
|
198
247
|
<Box flexDirection="column" marginTop={1}>
|
|
199
248
|
<Text color="gray">────────────────────────────────────────</Text>
|
|
@@ -201,18 +250,10 @@ export function RegisterAgentCommand({ agentName, model, systemPrompt }: Registe
|
|
|
201
250
|
<Box marginLeft={2}>
|
|
202
251
|
<Text color="red" wrap="wrap">{result.rejectReason}</Text>
|
|
203
252
|
</Box>
|
|
204
|
-
{result.benchmarkResult && result.benchmarkResult.failureReasons.length > 0 && (
|
|
205
|
-
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
|
206
|
-
<Text color="yellow" bold>Failure details:</Text>
|
|
207
|
-
{result.benchmarkResult.failureReasons.map((reason, i) => (
|
|
208
|
-
<Text key={i} color="yellow" wrap="wrap"> • {reason}</Text>
|
|
209
|
-
))}
|
|
210
|
-
</Box>
|
|
211
|
-
)}
|
|
212
253
|
</Box>
|
|
213
254
|
)}
|
|
214
255
|
|
|
215
|
-
{!result.rejected && steps.register === 'done' && (
|
|
256
|
+
{!result.rejected && !result.alreadyRegistered && steps.register === 'done' && (
|
|
216
257
|
<Box flexDirection="column" marginTop={1}>
|
|
217
258
|
<Text color="gray">────────────────────────────────────────</Text>
|
|
218
259
|
<Text color="green" bold>✓ Agent "{agentName}" registered successfully</Text>
|
|
@@ -105,13 +105,14 @@ function Help() {
|
|
|
105
105
|
<Header />
|
|
106
106
|
<Box flexDirection="column" marginLeft={2}>
|
|
107
107
|
<Text color="cyan" bold>Security commands:</Text>
|
|
108
|
-
<Text> opm push [--token t] [--otp c] Sign, scan, publish, register</Text>
|
|
109
|
-
<Text> opm install [pkg]
|
|
108
|
+
<Text> opm push [--token t] [--otp c] Sign, scan, publish, register + ENS records</Text>
|
|
109
|
+
<Text> opm install [pkg[@ver]] Install with on-chain security verification</Text>
|
|
110
|
+
<Text> opm install pkg@ens.eth Install safest version by ENS author</Text>
|
|
110
111
|
<Text> opm check Scan all deps: typosquats, CVEs, AI analysis</Text>
|
|
111
112
|
<Text> opm fix Auto-fix typosquats and vulnerable versions</Text>
|
|
112
113
|
<Text> opm audit Scan all deps against on-chain security data</Text>
|
|
113
|
-
<Text> opm info {'<pkg>'} Show on-chain
|
|
114
|
-
<Text> opm view {'<name.eth>'}
|
|
114
|
+
<Text> opm info {'<pkg>'} Show on-chain + ENS record data for a package</Text>
|
|
115
|
+
<Text> opm view {'<name.eth>'} Author profile, OPM records, packages</Text>
|
|
115
116
|
<Text> opm whois {'<name>'} Look up an ENS identity on OPM</Text>
|
|
116
117
|
<Text> </Text>
|
|
117
118
|
<Text color="cyan" bold>Agent commands:</Text>
|
|
@@ -134,7 +135,8 @@ function Help() {
|
|
|
134
135
|
<Text> opm pack Create a tarball</Text>
|
|
135
136
|
<Text> </Text>
|
|
136
137
|
<Text color="gray">Aliases: i/add → install, rm → uninstall, ls → list</Text>
|
|
137
|
-
<Text color="gray"> view name.eth → author profile
|
|
138
|
+
<Text color="gray"> view name.eth → author profile + OPM ENS records</Text>
|
|
139
|
+
<Text color="gray"> pkg@name.eth → ENS-resolved safest version by author</Text>
|
|
138
140
|
<Text> </Text>
|
|
139
141
|
<Text color="cyan" bold>Environment (install/audit/info/view need no config):</Text>
|
|
140
142
|
<Text> OPM_SIGNING_KEY Author signing key (for push only)</Text>
|