claude-flow 3.7.0 → 3.9.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/.claude/worktrees/adr-120-impl/v3/@claude-flow/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/README.md +4 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/README.md +4 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +492 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +39 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/wasm-agent-tools.d.ts +4 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/wasm-agent-tools.js +549 -0
- package/v3/@claude-flow/cli/dist/src/memory/embedding-quantization.d.ts +62 -0
- package/v3/@claude-flow/cli/dist/src/memory/embedding-quantization.js +147 -0
- package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.d.ts +61 -0
- package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.js +183 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +37 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.d.ts +46 -11
- package/v3/@claude-flow/cli/dist/src/ruvector/agent-wasm.js +134 -25
- package/v3/@claude-flow/cli/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/__probe.d.ts +0 -2
- package/v3/@claude-flow/cli/dist/src/__probe.js +0 -5
- package/v3/@claude-flow/cli/dist/src/commands/benchmark-cosign.d.ts +0 -29
- package/v3/@claude-flow/cli/dist/src/commands/benchmark-cosign.js +0 -222
- package/v3/@claude-flow/cli/dist/src/commands/benchmark-verify.d.ts +0 -21
- package/v3/@claude-flow/cli/dist/src/commands/benchmark-verify.js +0 -202
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-consensus-runtime.d.ts +0 -149
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-consensus-runtime.js +0 -296
- package/v3/@claude-flow/cli/dist/src/memory/ann-router-registry.d.ts +0 -61
- package/v3/@claude-flow/cli/dist/src/memory/ann-router-registry.js +0 -72
- package/v3/@claude-flow/cli/dist/src/memory/diskann-registry.d.ts +0 -56
- package/v3/@claude-flow/cli/dist/src/memory/diskann-registry.js +0 -88
|
@@ -68,6 +68,10 @@ export async function createWasmAgent(config = {}) {
|
|
|
68
68
|
max_turns: config.maxTurns ?? 50,
|
|
69
69
|
});
|
|
70
70
|
const agent = new mod.WasmAgent(configJson);
|
|
71
|
+
// ADR-129 P1 — wire JsModelProvider so the WASM runtime routes prompts
|
|
72
|
+
// through the v3 provider system instead of returning the echo stub.
|
|
73
|
+
// attachJsModelProvider is a no-op when no provider keys are set.
|
|
74
|
+
await attachJsModelProvider(agent, config);
|
|
71
75
|
const id = generateId();
|
|
72
76
|
const info = {
|
|
73
77
|
id,
|
|
@@ -82,19 +86,52 @@ export async function createWasmAgent(config = {}) {
|
|
|
82
86
|
agents.set(id, { agent, info });
|
|
83
87
|
return info;
|
|
84
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Wire a JsModelProvider to a freshly created WasmAgent so its internal
|
|
91
|
+
* conversation loop dispatches through the v3 provider system (ADR-129 P1).
|
|
92
|
+
*
|
|
93
|
+
* The callback bridges the JsModelProvider JSON contract to
|
|
94
|
+
* callAnthropicMessages, which already handles Anthropic / OpenRouter /
|
|
95
|
+
* Ollama routing via RUFLO_PROVIDER + key-presence precedence (#2042).
|
|
96
|
+
*
|
|
97
|
+
* Called once at agent-creation time; the provider stays attached for the
|
|
98
|
+
* agent's lifetime. No-op (returns false) when no provider keys are
|
|
99
|
+
* configured so the echo-fallback path below is preserved for keyless
|
|
100
|
+
* environments.
|
|
101
|
+
*/
|
|
102
|
+
async function attachJsModelProvider(agent, config) {
|
|
103
|
+
const hasAny = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENROUTER_API_KEY || process.env.OLLAMA_API_KEY);
|
|
104
|
+
if (!hasAny)
|
|
105
|
+
return false;
|
|
106
|
+
const mod = await import('@ruvector/rvagent-wasm');
|
|
107
|
+
const { callAnthropicMessages, resolveAnthropicModel } = await import('../mcp-tools/agent-execute-core.js');
|
|
108
|
+
const model = resolveAnthropicModel(config.model);
|
|
109
|
+
const systemPrompt = config.instructions || 'You are a helpful coding assistant running in a Ruflo WASM agent sandbox.';
|
|
110
|
+
const provider = new mod.JsModelProvider(async (messagesJson) => {
|
|
111
|
+
const messages = JSON.parse(messagesJson);
|
|
112
|
+
const lastUser = [...messages].reverse().find(m => m.role === 'user');
|
|
113
|
+
const prompt = lastUser?.content ?? messagesJson;
|
|
114
|
+
const result = await callAnthropicMessages({ prompt, systemPrompt, model, maxTokens: 2048 });
|
|
115
|
+
if (!result.success)
|
|
116
|
+
throw new Error(result.error ?? 'provider call failed');
|
|
117
|
+
return JSON.stringify({ role: 'assistant', content: result.output ?? '' });
|
|
118
|
+
});
|
|
119
|
+
agent.set_model_provider(provider);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
85
122
|
/**
|
|
86
123
|
* Send a prompt to a WASM agent.
|
|
87
124
|
*
|
|
88
|
-
* ADR-
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
125
|
+
* ADR-129 P1: JsModelProvider is now wired at creation time so the WASM
|
|
126
|
+
* agent's internal conversation loop (multi-turn state, turn_count,
|
|
127
|
+
* stop conditions) runs against a real LLM. The echo-stub detection
|
|
128
|
+
* block is kept as a fallback for keyless environments (CI, sandboxed
|
|
129
|
+
* test runners) — behaviour is identical to the pre-P1 path when no
|
|
130
|
+
* provider key is set.
|
|
94
131
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
132
|
+
* Billing note: every wasm_agent_prompt call with a provider key
|
|
133
|
+
* configured makes a billable LLM call. Use a keyless environment to
|
|
134
|
+
* get the echo stub for cost-free sandboxing.
|
|
98
135
|
*/
|
|
99
136
|
export async function promptWasmAgent(agentId, input) {
|
|
100
137
|
const entry = agents.get(agentId);
|
|
@@ -105,30 +142,31 @@ export async function promptWasmAgent(agentId, input) {
|
|
|
105
142
|
const wasmResult = await entry.agent.prompt(input);
|
|
106
143
|
entry.info.state = 'idle';
|
|
107
144
|
syncAgentInfo(entry);
|
|
108
|
-
// Detect the WASM echo stub
|
|
145
|
+
// Detect the WASM echo stub (present when no JsModelProvider was
|
|
146
|
+
// attached, i.e. keyless environments).
|
|
109
147
|
const isEchoStub = typeof wasmResult === 'string' &&
|
|
110
148
|
(wasmResult === `echo: ${input}` || /^echo: /.test(wasmResult.slice(0, 12)));
|
|
111
149
|
if (!isEchoStub) {
|
|
150
|
+
// JsModelProvider routed through the v3 provider system — return
|
|
151
|
+
// the real response. turn_count was already incremented by the
|
|
152
|
+
// WASM runtime.
|
|
112
153
|
return wasmResult;
|
|
113
154
|
}
|
|
114
|
-
// Echo stub
|
|
115
|
-
if (!process.env.ANTHROPIC_API_KEY) {
|
|
116
|
-
|
|
117
|
-
return `${wasmResult}\n[NOTE: bundled WASM agent has no LLM; set ANTHROPIC_API_KEY to enable real responses via Anthropic Messages API]`;
|
|
155
|
+
// Echo stub path (keyless fallback — preserved from pre-P1 behaviour).
|
|
156
|
+
if (!process.env.ANTHROPIC_API_KEY && !process.env.OPENROUTER_API_KEY && !process.env.OLLAMA_API_KEY) {
|
|
157
|
+
return `${wasmResult}\n[NOTE: bundled WASM agent has no LLM; set ANTHROPIC_API_KEY (or OPENROUTER_API_KEY / OLLAMA_API_KEY) to enable real responses via the v3 provider system]`;
|
|
118
158
|
}
|
|
159
|
+
// Key present but provider was not attached at creation time (e.g.
|
|
160
|
+
// agent created before a key was set in the environment). Fall
|
|
161
|
+
// through to a direct callAnthropicMessages call as a best-effort
|
|
162
|
+
// recovery.
|
|
119
163
|
const { callAnthropicMessages, resolveAnthropicModel } = await import('../mcp-tools/agent-execute-core.js');
|
|
120
164
|
const model = resolveAnthropicModel(entry.info.config.model);
|
|
121
165
|
const systemPrompt = entry.info.config.instructions || 'You are a helpful coding assistant running in a Ruflo WASM agent sandbox.';
|
|
122
|
-
const result = await callAnthropicMessages({
|
|
123
|
-
prompt: input,
|
|
124
|
-
systemPrompt,
|
|
125
|
-
model,
|
|
126
|
-
maxTokens: 2048,
|
|
127
|
-
});
|
|
166
|
+
const result = await callAnthropicMessages({ prompt: input, systemPrompt, model, maxTokens: 2048 });
|
|
128
167
|
if (!result.success) {
|
|
129
|
-
return `${wasmResult}\n[NOTE: bundled WASM agent has no LLM;
|
|
168
|
+
return `${wasmResult}\n[NOTE: bundled WASM agent has no LLM; provider fallback failed: ${result.error}]`;
|
|
130
169
|
}
|
|
131
|
-
// Return the real LLM output, not the echo stub.
|
|
132
170
|
return result.output ?? '';
|
|
133
171
|
}
|
|
134
172
|
catch (err) {
|
|
@@ -318,10 +356,12 @@ export async function createAgentFromTemplate(templateId) {
|
|
|
318
356
|
model: undefined, // Use default
|
|
319
357
|
});
|
|
320
358
|
}
|
|
321
|
-
// ── RVF Container Operations ─────────────────────────────────
|
|
322
359
|
/**
|
|
323
|
-
* Build an RVF container with prompts, tools, and
|
|
324
|
-
* Uses the high-level RVF builder API (addPrompt, addTool, addSkill).
|
|
360
|
+
* Build an RVF container with prompts, tools, skills, and MCP tool descriptors.
|
|
361
|
+
* Uses the high-level RVF builder API (addPrompt, addTool, addSkill, addMcpTools).
|
|
362
|
+
*
|
|
363
|
+
* ADR-129 P2: mcpTools parameter wires builder.addMcpTools() so that
|
|
364
|
+
* composed agents can declare which of ruflo's 314 MCP tools they need.
|
|
325
365
|
*/
|
|
326
366
|
export async function buildRvfContainer(opts) {
|
|
327
367
|
await initAgentWasm();
|
|
@@ -336,10 +376,78 @@ export async function buildRvfContainer(opts) {
|
|
|
336
376
|
for (const s of opts.skills ?? []) {
|
|
337
377
|
builder.addSkill(JSON.stringify(s));
|
|
338
378
|
}
|
|
379
|
+
// ADR-129 P2: pass MCP tool descriptors into the RVF container so
|
|
380
|
+
// composed agents know which tools are available via the MCP server.
|
|
381
|
+
if (opts.mcpTools && opts.mcpTools.length > 0) {
|
|
382
|
+
builder.addMcpTools(JSON.stringify(opts.mcpTools));
|
|
383
|
+
}
|
|
339
384
|
return builder.build();
|
|
340
385
|
}
|
|
386
|
+
// ── ADR-129 P3 — Additional gallery methods ──────────────────────────────────
|
|
387
|
+
/** Load a template as raw RVF bytes. */
|
|
388
|
+
export async function galleryLoadRvf(id) {
|
|
389
|
+
const gallery = await getGallery();
|
|
390
|
+
return gallery.loadRvf(id);
|
|
391
|
+
}
|
|
392
|
+
/** Apply configuration overrides to the active template. */
|
|
393
|
+
export async function galleryConfigure(configJson) {
|
|
394
|
+
const gallery = await getGallery();
|
|
395
|
+
gallery.configure(configJson);
|
|
396
|
+
}
|
|
397
|
+
/** List templates filtered by category. */
|
|
398
|
+
export async function galleryListByCategory(category) {
|
|
399
|
+
const gallery = await getGallery();
|
|
400
|
+
return gallery.listByCategory(category);
|
|
401
|
+
}
|
|
402
|
+
/** Add a custom template to the gallery. */
|
|
403
|
+
export async function galleryAddCustom(templateJson) {
|
|
404
|
+
const gallery = await getGallery();
|
|
405
|
+
gallery.addCustom(templateJson);
|
|
406
|
+
}
|
|
407
|
+
/** Remove a custom template by ID. */
|
|
408
|
+
export async function galleryRemoveCustom(id) {
|
|
409
|
+
const gallery = await getGallery();
|
|
410
|
+
gallery.removeCustom(id);
|
|
411
|
+
}
|
|
412
|
+
/** Import custom templates from JSON. Returns the count imported. */
|
|
413
|
+
export async function galleryImportCustom(templatesJson) {
|
|
414
|
+
const gallery = await getGallery();
|
|
415
|
+
return gallery.importCustom(templatesJson);
|
|
416
|
+
}
|
|
417
|
+
/** Export all custom templates as JSON. */
|
|
418
|
+
export async function galleryExportCustom() {
|
|
419
|
+
const gallery = await getGallery();
|
|
420
|
+
return gallery.exportCustom();
|
|
421
|
+
}
|
|
422
|
+
/** Get the currently active template ID. */
|
|
423
|
+
export async function galleryGetActive() {
|
|
424
|
+
const gallery = await getGallery();
|
|
425
|
+
return gallery.getActive();
|
|
426
|
+
}
|
|
427
|
+
/** Get configuration overrides for the active template. */
|
|
428
|
+
export async function galleryGetConfig() {
|
|
429
|
+
const gallery = await getGallery();
|
|
430
|
+
return gallery.getConfig();
|
|
431
|
+
}
|
|
432
|
+
/** Reset a WASM agent — clears messages and turn count. */
|
|
433
|
+
export function resetWasmAgent(agentId) {
|
|
434
|
+
const entry = agents.get(agentId);
|
|
435
|
+
if (!entry)
|
|
436
|
+
return false;
|
|
437
|
+
try {
|
|
438
|
+
entry.agent.reset();
|
|
439
|
+
syncAgentInfo(entry);
|
|
440
|
+
}
|
|
441
|
+
catch { /* best-effort */ }
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
341
444
|
/**
|
|
342
445
|
* Build an RVF container from a gallery template.
|
|
446
|
+
*
|
|
447
|
+
* ADR-129 P2: template.mcp_tools is now passed to buildRvfContainer so it
|
|
448
|
+
* is included via builder.addMcpTools(). Previously these descriptors were
|
|
449
|
+
* silently dropped, leaving gallery-template agents unable to declare their
|
|
450
|
+
* intended MCP tool access.
|
|
343
451
|
*/
|
|
344
452
|
export async function buildRvfFromTemplate(templateId) {
|
|
345
453
|
const template = await getGalleryTemplate(templateId);
|
|
@@ -349,6 +457,7 @@ export async function buildRvfFromTemplate(templateId) {
|
|
|
349
457
|
prompts: template.prompts,
|
|
350
458
|
tools: template.tools,
|
|
351
459
|
skills: template.skills,
|
|
460
|
+
mcpTools: template.mcp_tools, // ADR-129 P2: was silently dropped
|
|
352
461
|
});
|
|
353
462
|
}
|
|
354
463
|
//# sourceMappingURL=agent-wasm.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ADR-121 Phase 26 — `ruflo benchmark cosign` CLI subcommand.
|
|
3
|
-
*
|
|
4
|
-
* Phase 24 shipped the M-of-N cryptographic primitive (`coSign()`).
|
|
5
|
-
* Phase 25 shipped the consumer-facing verify command. This phase
|
|
6
|
-
* closes the loop with the **third-party-verifier workflow**:
|
|
7
|
-
*
|
|
8
|
-
* # vendor publishes ledger.json + a benchmark claim
|
|
9
|
-
* # third party reviews + co-signs
|
|
10
|
-
* npx ruflo benchmark cosign vendor-ledger.json \
|
|
11
|
-
* --entry 11 \
|
|
12
|
-
* --label "independent-auditor" \
|
|
13
|
-
* --out audited-ledger.json
|
|
14
|
-
*
|
|
15
|
-
* # downstream consumers check the audited ledger requires
|
|
16
|
-
* # two signatures per entry
|
|
17
|
-
* npx ruflo benchmark verify audited-ledger.json --threshold 2
|
|
18
|
-
*
|
|
19
|
-
* Auto-generates an ephemeral Ed25519 keypair (writes the public
|
|
20
|
-
* key alongside the cosignature in the output ledger; private key
|
|
21
|
-
* not persisted by default). Pass `--key <path>` to persist the
|
|
22
|
-
* keypair (or read from a previously-persisted file) so the same
|
|
23
|
-
* signer can attest multiple entries / multiple ledgers with a
|
|
24
|
-
* stable identity.
|
|
25
|
-
*/
|
|
26
|
-
import type { Command } from '../types.js';
|
|
27
|
-
export declare const benchmarkCosignCommand: Command;
|
|
28
|
-
export default benchmarkCosignCommand;
|
|
29
|
-
//# sourceMappingURL=benchmark-cosign.d.ts.map
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ADR-121 Phase 26 — `ruflo benchmark cosign` CLI subcommand.
|
|
3
|
-
*
|
|
4
|
-
* Phase 24 shipped the M-of-N cryptographic primitive (`coSign()`).
|
|
5
|
-
* Phase 25 shipped the consumer-facing verify command. This phase
|
|
6
|
-
* closes the loop with the **third-party-verifier workflow**:
|
|
7
|
-
*
|
|
8
|
-
* # vendor publishes ledger.json + a benchmark claim
|
|
9
|
-
* # third party reviews + co-signs
|
|
10
|
-
* npx ruflo benchmark cosign vendor-ledger.json \
|
|
11
|
-
* --entry 11 \
|
|
12
|
-
* --label "independent-auditor" \
|
|
13
|
-
* --out audited-ledger.json
|
|
14
|
-
*
|
|
15
|
-
* # downstream consumers check the audited ledger requires
|
|
16
|
-
* # two signatures per entry
|
|
17
|
-
* npx ruflo benchmark verify audited-ledger.json --threshold 2
|
|
18
|
-
*
|
|
19
|
-
* Auto-generates an ephemeral Ed25519 keypair (writes the public
|
|
20
|
-
* key alongside the cosignature in the output ledger; private key
|
|
21
|
-
* not persisted by default). Pass `--key <path>` to persist the
|
|
22
|
-
* keypair (or read from a previously-persisted file) so the same
|
|
23
|
-
* signer can attest multiple entries / multiple ledgers with a
|
|
24
|
-
* stable identity.
|
|
25
|
-
*/
|
|
26
|
-
import { promises as fs } from 'node:fs';
|
|
27
|
-
import { resolve } from 'node:path';
|
|
28
|
-
import { generateKeyPairSync, createPrivateKey, createPublicKey } from 'node:crypto';
|
|
29
|
-
import { output } from '../output.js';
|
|
30
|
-
async function loadOrGenerateKeypair(keyPath) {
|
|
31
|
-
if (keyPath) {
|
|
32
|
-
const abs = resolve(process.cwd(), keyPath);
|
|
33
|
-
try {
|
|
34
|
-
const raw = await fs.readFile(abs, 'utf8');
|
|
35
|
-
const parsed = JSON.parse(raw);
|
|
36
|
-
const privateKey = createPrivateKey({ key: Buffer.from(parsed.privateKey, 'hex'), format: 'der', type: 'pkcs8' });
|
|
37
|
-
const publicKey = createPublicKey({ key: Buffer.from(parsed.publicKey, 'hex'), format: 'der', type: 'spki' });
|
|
38
|
-
return { keypair: { privateKey, publicKey }, source: 'loaded' };
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
// File doesn't exist yet — generate a fresh keypair and persist it.
|
|
42
|
-
const kp = generateKeyPairSync('ed25519');
|
|
43
|
-
const pkcs8 = kp.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('hex');
|
|
44
|
-
const spki = kp.publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
|
|
45
|
-
await fs.writeFile(abs, JSON.stringify({ privateKey: pkcs8, publicKey: spki }, null, 2));
|
|
46
|
-
return { keypair: kp, source: 'persisted' };
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return { keypair: generateKeyPairSync('ed25519'), source: 'generated' };
|
|
50
|
-
}
|
|
51
|
-
export const benchmarkCosignCommand = {
|
|
52
|
-
name: 'cosign',
|
|
53
|
-
description: 'Add a third-party co-signature to an entry in a benchmark ledger (Phase 24 M-of-N attestation)',
|
|
54
|
-
options: [
|
|
55
|
-
{
|
|
56
|
-
name: 'entry',
|
|
57
|
-
short: 'e',
|
|
58
|
-
type: 'number',
|
|
59
|
-
description: 'Entry sequence number to co-sign (1-based). Default: last entry.',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'label',
|
|
63
|
-
short: 'l',
|
|
64
|
-
type: 'string',
|
|
65
|
-
description: 'Human-readable label for this signer (e.g. "third-party-verifier")',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'out',
|
|
69
|
-
short: 'o',
|
|
70
|
-
type: 'string',
|
|
71
|
-
description: 'Output path for the updated ledger. Default: overwrite the input.',
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: 'key',
|
|
75
|
-
short: 'k',
|
|
76
|
-
type: 'string',
|
|
77
|
-
description: 'Path to a JSON keypair file (pkcs8 + spki hex). If missing, a fresh ephemeral key is generated; if path doesn\'t exist, a new key is generated and persisted there.',
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'all',
|
|
81
|
-
type: 'boolean',
|
|
82
|
-
description: 'Co-sign EVERY entry in the ledger (useful for batch attestation by a single signer).',
|
|
83
|
-
default: 'false',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'json',
|
|
87
|
-
type: 'boolean',
|
|
88
|
-
description: 'Output JSON instead of human-readable',
|
|
89
|
-
default: 'false',
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
examples: [
|
|
93
|
-
{ command: 'ruflo benchmark cosign ledger.json --entry 11 --label "auditor-A"', description: 'Co-sign entry 11 with an ephemeral key' },
|
|
94
|
-
{ command: 'ruflo benchmark cosign ledger.json --all --label "release-gate" --key ./auditor.key.json', description: 'Co-sign every entry with a persisted key' },
|
|
95
|
-
{ command: 'ruflo benchmark cosign ledger.json -e 11 -o audited.json', description: 'Write the cosigned ledger to a new file' },
|
|
96
|
-
],
|
|
97
|
-
action: async (ctx) => {
|
|
98
|
-
const pathArg = ctx.args[0];
|
|
99
|
-
const asJson = ctx.flags.json === true || ctx.flags.json === 'true';
|
|
100
|
-
const all = ctx.flags.all === true || ctx.flags.all === 'true';
|
|
101
|
-
const entryFlag = ctx.flags.entry !== undefined ? Number(ctx.flags.entry) : undefined;
|
|
102
|
-
const label = ctx.flags.label;
|
|
103
|
-
const outPath = ctx.flags.out;
|
|
104
|
-
const keyPath = ctx.flags.key;
|
|
105
|
-
if (!pathArg || typeof pathArg !== 'string') {
|
|
106
|
-
const err = 'usage: ruflo benchmark cosign <path-to-ledger.json> [--entry N | --all] [--label "name"] [--out path] [--key path] [--json]';
|
|
107
|
-
if (asJson)
|
|
108
|
-
output.printJson({ ok: false, error: err });
|
|
109
|
-
else
|
|
110
|
-
output.printError(err);
|
|
111
|
-
return { success: false, exitCode: 1 };
|
|
112
|
-
}
|
|
113
|
-
if (!all && entryFlag !== undefined && (!Number.isFinite(entryFlag) || entryFlag < 1)) {
|
|
114
|
-
const err = `--entry must be a positive integer, got: ${ctx.flags.entry}`;
|
|
115
|
-
if (asJson)
|
|
116
|
-
output.printJson({ ok: false, error: err });
|
|
117
|
-
else
|
|
118
|
-
output.printError(err);
|
|
119
|
-
return { success: false, exitCode: 1 };
|
|
120
|
-
}
|
|
121
|
-
const inPath = resolve(process.cwd(), pathArg);
|
|
122
|
-
let raw;
|
|
123
|
-
try {
|
|
124
|
-
raw = await fs.readFile(inPath, 'utf8');
|
|
125
|
-
}
|
|
126
|
-
catch (err) {
|
|
127
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
128
|
-
if (asJson)
|
|
129
|
-
output.printJson({ ok: false, error: `cannot read ${inPath}: ${msg}` });
|
|
130
|
-
else
|
|
131
|
-
output.printError(`Cannot read ${inPath}: ${msg}`);
|
|
132
|
-
return { success: false, exitCode: 1 };
|
|
133
|
-
}
|
|
134
|
-
let ledger;
|
|
135
|
-
try {
|
|
136
|
-
ledger = JSON.parse(raw);
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
140
|
-
if (asJson)
|
|
141
|
-
output.printJson({ ok: false, error: `not valid JSON: ${msg}` });
|
|
142
|
-
else
|
|
143
|
-
output.printError(`Not valid JSON: ${msg}`);
|
|
144
|
-
return { success: false, exitCode: 1 };
|
|
145
|
-
}
|
|
146
|
-
if (typeof ledger.version !== 'number' || !Array.isArray(ledger.entries) || ledger.entries.length === 0) {
|
|
147
|
-
const err = `not a benchmark ledger — expected { version: number, entries: [non-empty] }`;
|
|
148
|
-
if (asJson)
|
|
149
|
-
output.printJson({ ok: false, error: err });
|
|
150
|
-
else
|
|
151
|
-
output.printError(err);
|
|
152
|
-
return { success: false, exitCode: 1 };
|
|
153
|
-
}
|
|
154
|
-
// Load or generate the signing keypair.
|
|
155
|
-
const { keypair, source: keypairSource } = await loadOrGenerateKeypair(keyPath);
|
|
156
|
-
// Determine which entries to co-sign.
|
|
157
|
-
let targetIndices;
|
|
158
|
-
if (all) {
|
|
159
|
-
targetIndices = ledger.entries.map((_, i) => i);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
const seq = entryFlag ?? ledger.entries[ledger.entries.length - 1].sequence;
|
|
163
|
-
const idx = ledger.entries.findIndex(e => e.sequence === seq);
|
|
164
|
-
if (idx === -1) {
|
|
165
|
-
const err = `entry sequence ${seq} not found in ledger (chain has ${ledger.entries.length} entries with sequences 1..${ledger.entries.length})`;
|
|
166
|
-
if (asJson)
|
|
167
|
-
output.printJson({ ok: false, error: err });
|
|
168
|
-
else
|
|
169
|
-
output.printError(err);
|
|
170
|
-
return { success: false, exitCode: 1 };
|
|
171
|
-
}
|
|
172
|
-
targetIndices = [idx];
|
|
173
|
-
}
|
|
174
|
-
// Lazy-load the cosign primitive from the published embeddings package.
|
|
175
|
-
const { coSign } = await import('@claude-flow/embeddings/witness-ledger');
|
|
176
|
-
// Mutate a copy of the ledger.
|
|
177
|
-
const newEntries = ledger.entries.map((e, i) => targetIndices.includes(i)
|
|
178
|
-
? coSign(e, keypair, label ? { signerLabel: label } : {})
|
|
179
|
-
: e);
|
|
180
|
-
const newLedger = { ...ledger, entries: newEntries };
|
|
181
|
-
// Write output.
|
|
182
|
-
const writePath = resolve(process.cwd(), outPath ?? pathArg);
|
|
183
|
-
await fs.writeFile(writePath, JSON.stringify(newLedger, null, 2));
|
|
184
|
-
const publicKeyHex = keypair.publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
|
|
185
|
-
if (asJson) {
|
|
186
|
-
output.printJson({
|
|
187
|
-
ok: true,
|
|
188
|
-
inPath,
|
|
189
|
-
outPath: writePath,
|
|
190
|
-
entriesCosigned: targetIndices.length,
|
|
191
|
-
targetSequences: targetIndices.map(i => ledger.entries[i].sequence),
|
|
192
|
-
signerLabel: label ?? null,
|
|
193
|
-
publicKey: publicKeyHex,
|
|
194
|
-
keypairSource,
|
|
195
|
-
});
|
|
196
|
-
return { success: true, exitCode: 0 };
|
|
197
|
-
}
|
|
198
|
-
output.writeln();
|
|
199
|
-
output.writeln(output.bold(`Co-signed ${targetIndices.length} entr${targetIndices.length === 1 ? 'y' : 'ies'}`));
|
|
200
|
-
output.writeln(output.dim('─'.repeat(60)));
|
|
201
|
-
output.writeln(` input: ${inPath}`);
|
|
202
|
-
output.writeln(` output: ${writePath}`);
|
|
203
|
-
output.writeln(` signer label: ${label ?? '(unlabeled)'}`);
|
|
204
|
-
output.writeln(` signer pubkey: ${publicKeyHex.slice(0, 32)}...`);
|
|
205
|
-
output.writeln(` keypair: ${keypairSource}${keyPath ? ` → ${keyPath}` : ''}`);
|
|
206
|
-
output.writeln();
|
|
207
|
-
output.writeln(' entries:');
|
|
208
|
-
for (const i of targetIndices) {
|
|
209
|
-
const e = newEntries[i];
|
|
210
|
-
const cosigCount = Array.isArray(e.cosignatures) ? e.cosignatures.length : 0;
|
|
211
|
-
const hashShort = e.contentHash.slice(0, 12) + '…';
|
|
212
|
-
output.writeln(` [${String(e.sequence).padStart(2)}] ${e.benchmark.padEnd(28)} now ${1 + cosigCount} sigs (${hashShort})`);
|
|
213
|
-
}
|
|
214
|
-
output.writeln();
|
|
215
|
-
output.writeln(`Next: verify the cosigned ledger with the new threshold:`);
|
|
216
|
-
output.writeln(output.dim(` npx ruflo benchmark verify ${writePath} --threshold 2`));
|
|
217
|
-
output.writeln();
|
|
218
|
-
return { success: true, exitCode: 0 };
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
export default benchmarkCosignCommand;
|
|
222
|
-
//# sourceMappingURL=benchmark-cosign.js.map
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ADR-121 Phase 25 — `ruflo benchmark verify` CLI subcommand.
|
|
3
|
-
*
|
|
4
|
-
* Makes the Phase 15-24 witness story end-user-accessible. Anyone
|
|
5
|
-
* publishing benchmark numbers with a witness manifest (single
|
|
6
|
-
* `.json`) or a chained ledger (`bench-witness/ledger.json`) can
|
|
7
|
-
* tell consumers:
|
|
8
|
-
*
|
|
9
|
-
* npx ruflo benchmark verify ./ledger.json
|
|
10
|
-
*
|
|
11
|
-
* The command auto-detects whether the input is a single witness or
|
|
12
|
-
* a ledger (presence of `version` + `entries[]` → ledger), runs the
|
|
13
|
-
* appropriate verifier, and prints a human-readable or JSON report.
|
|
14
|
-
*
|
|
15
|
-
* For Phase 24 multi-signer entries, pass `--threshold N` to require
|
|
16
|
-
* N or more valid signatures per entry.
|
|
17
|
-
*/
|
|
18
|
-
import type { Command } from '../types.js';
|
|
19
|
-
export declare const benchmarkVerifyCommand: Command;
|
|
20
|
-
export default benchmarkVerifyCommand;
|
|
21
|
-
//# sourceMappingURL=benchmark-verify.d.ts.map
|