feat: P5.5 dev inject + adapter-inspector demo + adapter smoke (P5 complete)

Dev-only POST /v1/dev/webhook/:type signs+injects a sample payload. apps/adapter-
inspector (Vite): inbound raw events + outbound commands/attempts, simulate-inbound
+ test-send buttons. smoke-adapter proves signed->normalized->interaction, bad-sig
401, sandboxed outbound (no network). 55 tests; all 4 smokes pass; boundary enforced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-01 13:54:18 +05:30
parent 4f661f4c81
commit b879c9eba2
9 changed files with 288 additions and 5 deletions
@@ -1,16 +1,23 @@
import { BadRequestException, Body, Controller, ForbiddenException, Post } from '@nestjs/common';
import { randomUUID } from 'node:crypto';
import { BadRequestException, Body, Controller, ForbiddenException, Param, Post } from '@nestjs/common';
import jwt from 'jsonwebtoken';
import { hmacSign } from '@insignia/iios-adapter-sdk';
import { InboundService } from '../adapters/inbound.service';
import { AdapterRegistry } from '../adapters/adapter.registry';
/**
* Dev-only helper: mints an HS256 token a host app would normally sign, so the
* browser demo can authenticate. Gated by IIOS_DEV_TOKENS=1 — never enable in
* production (the host app signs its own tokens there).
* Dev-only helpers (gated by IIOS_DEV_TOKENS=1). Never enable in production.
*/
@Controller('v1/dev')
export class DevController {
constructor(
private readonly inbound: InboundService,
private readonly registry: AdapterRegistry,
) {}
@Post('token')
token(@Body() body: { appId: string; userId: string; name?: string; orgId?: string }): { token: string } {
if (process.env.IIOS_DEV_TOKENS !== '1') throw new ForbiddenException('dev tokens disabled');
this.assertDev();
let secrets: Record<string, string>;
try {
secrets = JSON.parse(process.env.APP_SECRETS ?? '{}');
@@ -26,4 +33,29 @@ export class DevController {
);
return { token };
}
/** Server signs a sample provider payload and feeds it to the inbound pipeline. */
@Post('webhook/:channelType')
async injectWebhook(@Param('channelType') channelType: string, @Body() body?: { from?: string; text?: string }) {
this.assertDev();
const reg = this.registry.get(channelType);
if (!reg) throw new BadRequestException(`unknown channel type: ${channelType}`);
const payload = {
eventId: `dev-${randomUUID()}`,
from: body?.from ?? 'sim@customer',
displayName: 'Sim Customer',
threadRef: `sim-${randomUUID().slice(0, 8)}`,
subject: 'Simulated inbound',
text: body?.text ?? 'Hello from a simulated provider',
};
const raw = JSON.stringify(payload);
return this.inbound.receive(channelType, raw, {
'x-iios-signature': hmacSign(raw, reg.config.secret),
'content-type': 'application/json',
});
}
private assertDev(): void {
if (process.env.IIOS_DEV_TOKENS !== '1') throw new ForbiddenException('dev endpoints disabled');
}
}