@agfpd/iapeer 0.4.112 → 0.4.114
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 +1 -1
- package/src/cli/index.ts +39 -10
- package/src/init/index.ts +12 -0
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -450,7 +450,7 @@ export async function addRuntime(
|
|
|
450
450
|
delete cleanEnv.PEER_PERSONALITY
|
|
451
451
|
delete cleanEnv.PEER_RUNTIME
|
|
452
452
|
delete cleanEnv.PEER_IDENTITY
|
|
453
|
-
const { initPeer } = await import('../init/index.ts')
|
|
453
|
+
const { initPeer, hasClaudeMcpWiring } = await import('../init/index.ts')
|
|
454
454
|
const out: AddRuntimeOutcome[] = []
|
|
455
455
|
for (const p of targets) {
|
|
456
456
|
// Skip an infra-DEFAULT peer only when the runtime being added is AGENTIC: giving a
|
|
@@ -467,9 +467,12 @@ export async function addRuntime(
|
|
|
467
467
|
// Short-circuiting on the declaration alone would report "already" while the channel
|
|
468
468
|
// does not exist — so for infra, converge: a missing plist means there IS work to do.
|
|
469
469
|
// initPeer is idempotent, so the repair costs nothing when everything is in place.
|
|
470
|
+
// Same for claude: declared on a codex-born peer ≠ its `.mcp.json` wiring exists.
|
|
470
471
|
const realized = isInfraRuntime(runtime)
|
|
471
472
|
? existsSync(resolveAlwaysOnTarget(p.personality, runtime as Runtime, env).path)
|
|
472
|
-
:
|
|
473
|
+
: runtime === 'claude'
|
|
474
|
+
? hasClaudeMcpWiring(p.cwd)
|
|
475
|
+
: true
|
|
473
476
|
if (p.runtimes.includes(runtime as Runtime) && realized) {
|
|
474
477
|
out.push({ personality: p.personality, action: 'already' })
|
|
475
478
|
continue
|
|
@@ -481,7 +484,10 @@ export async function addRuntime(
|
|
|
481
484
|
out.push({
|
|
482
485
|
personality: p.personality,
|
|
483
486
|
action: 'added',
|
|
484
|
-
detail:
|
|
487
|
+
detail:
|
|
488
|
+
[repair ? (isInfraRuntime(runtime) ? 'declared but no plist — channel re-provisioned' : 'declared but not wired — runtime re-provisioned') : undefined, ...warns]
|
|
489
|
+
.filter(Boolean)
|
|
490
|
+
.join(' | ') || undefined,
|
|
485
491
|
})
|
|
486
492
|
} catch (e) {
|
|
487
493
|
out.push({ personality: p.personality, action: 'failed', detail: e instanceof Error ? e.message : String(e) })
|
|
@@ -524,7 +530,14 @@ export async function defaultRuntime(
|
|
|
524
530
|
const index = readPeersIndex({ env })
|
|
525
531
|
const targets = opts.all === true ? index.peers : index.peers.filter(p => p.personality === opts.peer)
|
|
526
532
|
if (targets.length === 0) throw new Error(opts.peer ? `peer "${opts.peer}" is not registered` : 'no targets — pass --peer <p> or --all')
|
|
533
|
+
// Cross-peer: strip the caller's identity env (same reason as add-runtime).
|
|
534
|
+
const cleanEnv: NodeJS.ProcessEnv = { ...env }
|
|
535
|
+
delete cleanEnv.PEER_PERSONALITY
|
|
536
|
+
delete cleanEnv.PEER_RUNTIME
|
|
537
|
+
delete cleanEnv.PEER_IDENTITY
|
|
538
|
+
const { initPeer, hasClaudeMcpWiring } = await import('../init/index.ts')
|
|
527
539
|
const out: DefaultRuntimeOutcome[] = []
|
|
540
|
+
let rewired = false
|
|
528
541
|
for (const p of targets) {
|
|
529
542
|
if (isInfraRuntime(p.runtime)) {
|
|
530
543
|
out.push({ personality: p.personality, action: 'skipped-infra-peer', detail: `default runtime "${p.runtime}" is infra` })
|
|
@@ -536,10 +549,6 @@ export async function defaultRuntime(
|
|
|
536
549
|
out.push({ personality: p.personality, action: 'failed', detail: `no local profile at ${p.cwd}` })
|
|
537
550
|
continue
|
|
538
551
|
}
|
|
539
|
-
if (profile.runtime === runtime) {
|
|
540
|
-
out.push({ personality: p.personality, action: 'already' })
|
|
541
|
-
continue
|
|
542
|
-
}
|
|
543
552
|
if (!profile.runtimes.includes(runtime as Runtime)) {
|
|
544
553
|
out.push({
|
|
545
554
|
personality: p.personality,
|
|
@@ -548,15 +557,35 @@ export async function defaultRuntime(
|
|
|
548
557
|
})
|
|
549
558
|
continue
|
|
550
559
|
}
|
|
551
|
-
|
|
552
|
-
|
|
560
|
+
// DECLARED ≠ REALIZED (live 2026-10-17: the fleet flipped codex→claude onto peers
|
|
561
|
+
// that declared claude but were never wired — no `.mcp.json` iapeer server, no
|
|
562
|
+
// memory-slot surfaces → claude sessions without send_to_peer). Routing must never
|
|
563
|
+
// sit on an unwired runtime: converge FIRST via initPeer (idempotent: MCP wiring +
|
|
564
|
+
// slot provision for every declared agentic runtime) — also for an ALREADY-flipped
|
|
565
|
+
// peer, so re-running the verb is the repair for a fleet flipped by an older binary.
|
|
566
|
+
let detail: string | undefined
|
|
567
|
+
if (runtime === 'claude' && !hasClaudeMcpWiring(p.cwd)) {
|
|
568
|
+
const warns: string[] = []
|
|
569
|
+
await initPeer({ cwd: p.cwd, runtime: runtime as Runtime, env: cleanEnv, warn: m => warns.push(m) })
|
|
570
|
+
detail = ['declared but not wired — runtime re-provisioned', ...warns].join(' | ')
|
|
571
|
+
rewired = true
|
|
572
|
+
}
|
|
573
|
+
if (profile.runtime === runtime) {
|
|
574
|
+
out.push({ personality: p.personality, action: 'already', detail })
|
|
575
|
+
continue
|
|
576
|
+
}
|
|
577
|
+
// Re-read: initPeer's merge-writer may have touched the profile.
|
|
578
|
+
writePeerProfileAtomic(p.cwd, { ...(readPeerProfile(p.cwd) ?? profile), runtime: runtime as Runtime })
|
|
579
|
+
out.push({ personality: p.personality, action: 'flipped', detail })
|
|
553
580
|
} catch (e) {
|
|
554
581
|
out.push({ personality: p.personality, action: 'failed', detail: e instanceof Error ? e.message : String(e) })
|
|
555
582
|
}
|
|
556
583
|
}
|
|
557
584
|
// Self-heal the registry from the flipped locals so routing flips in the same
|
|
558
585
|
// command (REPLACE projection — the verify --fix rail).
|
|
559
|
-
|
|
586
|
+
// initPeer's upsert sets the registry default to the provisioned runtime — reindex
|
|
587
|
+
// after a rewire too, so an `already` peer's registry stays projected from its local.
|
|
588
|
+
if (rewired || out.some(o => o.action === 'flipped')) await reindexFromLocals({ env })
|
|
560
589
|
return out
|
|
561
590
|
}
|
|
562
591
|
|
package/src/init/index.ts
CHANGED
|
@@ -599,6 +599,18 @@ export async function initPeer(opts: InitPeerOptions): Promise<InitPeerResult> {
|
|
|
599
599
|
function hasClaudeMarker(cwd: string): boolean {
|
|
600
600
|
return existsSync(join(cwd, '.claude'))
|
|
601
601
|
}
|
|
602
|
+
/** Claude side REALIZED: the cwd's `.mcp.json` carries the foundation daemon server.
|
|
603
|
+
* A peer born codex-primary declares claude but never got this wiring (init wires
|
|
604
|
+
* claude only for a claude primary / `.claude` marker) → a claude session without
|
|
605
|
+
* send_to_peer (live, fleet switch 2026-10-17). */
|
|
606
|
+
export function hasClaudeMcpWiring(cwd: string): boolean {
|
|
607
|
+
try {
|
|
608
|
+
const doc = JSON.parse(readFileSync(join(cwd, '.mcp.json'), 'utf8')) as { mcpServers?: Record<string, unknown> }
|
|
609
|
+
return Boolean(doc.mcpServers?.[IAPEER_MCP_SERVER_NAME])
|
|
610
|
+
} catch {
|
|
611
|
+
return false
|
|
612
|
+
}
|
|
613
|
+
}
|
|
602
614
|
function hasCodexMarker(cwd: string): boolean {
|
|
603
615
|
return existsSync(join(cwd, '.codex'))
|
|
604
616
|
}
|