claude-flow 3.7.0 → 3.8.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.
@@ -86,16 +86,16 @@ export declare function createWasmAgent(config?: WasmAgentConfig): Promise<WasmA
86
86
  /**
87
87
  * Send a prompt to a WASM agent.
88
88
  *
89
- * ADR-095 G4: the bundled @ruvector/rvagent-wasm doesn't actually run an
90
- * LLM its prompt() method echoes input back as `"echo: <input>"`. We
91
- * detect that stub output and route the prompt through Anthropic's
92
- * Messages API so users get a real response. The WASM agent's sandbox
93
- * (virtual filesystem, tool execution) still works for non-LLM ops via
94
- * executeWasmTool we're just patching the "talk to a model" hole.
89
+ * ADR-129 P1: JsModelProvider is now wired at creation time so the WASM
90
+ * agent's internal conversation loop (multi-turn state, turn_count,
91
+ * stop conditions) runs against a real LLM. The echo-stub detection
92
+ * block is kept as a fallback for keyless environments (CI, sandboxed
93
+ * test runners) behaviour is identical to the pre-P1 path when no
94
+ * provider key is set.
95
95
  *
96
- * If ANTHROPIC_API_KEY is not set, returns the stub output verbatim so
97
- * the failure mode is obvious to the caller (matches the previous
98
- * behaviour rather than throwing for users without keys configured).
96
+ * Billing note: every wasm_agent_prompt call with a provider key
97
+ * configured makes a billable LLM call. Use a keyless environment to
98
+ * get the echo stub for cost-free sandboxing.
99
99
  */
100
100
  export declare function promptWasmAgent(agentId: string, input: string): Promise<string>;
101
101
  export declare function executeWasmTool(agentId: string, toolCall: Record<string, unknown>): Promise<ToolResult>;
@@ -163,9 +163,18 @@ export declare function getGalleryTemplate(id: string): Promise<GalleryTemplateD
163
163
  * Create an agent from a gallery template.
164
164
  */
165
165
  export declare function createAgentFromTemplate(templateId: string): Promise<WasmAgentInfo>;
166
+ export interface McpToolDescriptor {
167
+ name: string;
168
+ description: string;
169
+ input_schema: unknown;
170
+ group?: string;
171
+ }
166
172
  /**
167
- * Build an RVF container with prompts, tools, and skills.
168
- * Uses the high-level RVF builder API (addPrompt, addTool, addSkill).
173
+ * Build an RVF container with prompts, tools, skills, and MCP tool descriptors.
174
+ * Uses the high-level RVF builder API (addPrompt, addTool, addSkill, addMcpTools).
175
+ *
176
+ * ADR-129 P2: mcpTools parameter wires builder.addMcpTools() so that
177
+ * composed agents can declare which of ruflo's 314 MCP tools they need.
169
178
  */
170
179
  export declare function buildRvfContainer(opts: {
171
180
  prompts?: Array<{
@@ -185,9 +194,35 @@ export declare function buildRvfContainer(opts: {
185
194
  trigger: string;
186
195
  content: string;
187
196
  }>;
197
+ mcpTools?: McpToolDescriptor[];
188
198
  }): Promise<Uint8Array>;
199
+ /** Load a template as raw RVF bytes. */
200
+ export declare function galleryLoadRvf(id: string): Promise<Uint8Array>;
201
+ /** Apply configuration overrides to the active template. */
202
+ export declare function galleryConfigure(configJson: string): Promise<void>;
203
+ /** List templates filtered by category. */
204
+ export declare function galleryListByCategory(category: string): Promise<GalleryTemplate[]>;
205
+ /** Add a custom template to the gallery. */
206
+ export declare function galleryAddCustom(templateJson: string): Promise<void>;
207
+ /** Remove a custom template by ID. */
208
+ export declare function galleryRemoveCustom(id: string): Promise<void>;
209
+ /** Import custom templates from JSON. Returns the count imported. */
210
+ export declare function galleryImportCustom(templatesJson: string): Promise<number>;
211
+ /** Export all custom templates as JSON. */
212
+ export declare function galleryExportCustom(): Promise<unknown>;
213
+ /** Get the currently active template ID. */
214
+ export declare function galleryGetActive(): Promise<string | undefined>;
215
+ /** Get configuration overrides for the active template. */
216
+ export declare function galleryGetConfig(): Promise<unknown>;
217
+ /** Reset a WASM agent — clears messages and turn count. */
218
+ export declare function resetWasmAgent(agentId: string): boolean;
189
219
  /**
190
220
  * Build an RVF container from a gallery template.
221
+ *
222
+ * ADR-129 P2: template.mcp_tools is now passed to buildRvfContainer so it
223
+ * is included via builder.addMcpTools(). Previously these descriptors were
224
+ * silently dropped, leaving gallery-template agents unable to declare their
225
+ * intended MCP tool access.
191
226
  */
192
227
  export declare function buildRvfFromTemplate(templateId: string): Promise<Uint8Array>;
193
228
  //# sourceMappingURL=agent-wasm.d.ts.map
@@ -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-095 G4: the bundled @ruvector/rvagent-wasm doesn't actually run an
89
- * LLM its prompt() method echoes input back as `"echo: <input>"`. We
90
- * detect that stub output and route the prompt through Anthropic's
91
- * Messages API so users get a real response. The WASM agent's sandbox
92
- * (virtual filesystem, tool execution) still works for non-LLM ops via
93
- * executeWasmTool we're just patching the "talk to a model" hole.
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
- * If ANTHROPIC_API_KEY is not set, returns the stub output verbatim so
96
- * the failure mode is obvious to the caller (matches the previous
97
- * behaviour rather than throwing for users without keys configured).
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 detected route through a real LLM call.
115
- if (!process.env.ANTHROPIC_API_KEY) {
116
- // No key configured; surface the stub honestly with a hint.
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; Anthropic fallback failed: ${result.error}]`;
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 skills.
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.7.0",
3
+ "version": "3.8.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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=__probe.d.ts.map
@@ -1,5 +0,0 @@
1
- import * as e from '@claude-flow/embeddings';
2
- const fn = e.mmrRerank;
3
- const f2 = e.averagePairwiseSimilarity;
4
- console.log(fn, f2);
5
- //# sourceMappingURL=__probe.js.map
@@ -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