mirror of
https://github.com/hanzoai/extension.git
synced 2026-10-31 21:57:06 +00:00
A Bot Framework (botbuilder) app bringing Hanzo AI into Microsoft Teams, built on the shared foundation — @hanzo/ai (headless client), @hanzo/iam (identity), @hanzo/cards (canonical PanelSpec -> Adaptive Card). - Bot (TeamsActivityHandler): 1:1 + channel chat; @mention/DM a prompt -> completion -> reply as a Hanzo assistant Adaptive Card panel. - Adaptive Card actions: model picker (Input.ChoiceSet), quick actions (Draft/Summarize/Explain/Extract action items), prompt input + submit, all routed by dispatch() on data.action (from @hanzo/cards). - Message extension "Ask Hanzo" (action) on a message + "Compose with Hanzo" (query) to insert an AI draft; pulls message/thread context. - Teams app manifest (schema v1.16) + color/outline icons + a dependency- free zip build step for the sideloadable app package. - Pure logic modules (dispatch/panels/context/hanzo/identity/config) with vitest coverage; tsc clean; tsup build (library + runnable server). api.hanzo.ai /v1 via @hanzo/ai only; secrets are env-only (never committed).
56 lines
1.6 KiB
TypeScript
56 lines
1.6 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
parseSub,
|
|
identityFromClaims,
|
|
anonymous,
|
|
tenantIdentity,
|
|
} from '../src/identity';
|
|
|
|
describe('parseSub', () => {
|
|
it('splits org/username', () => {
|
|
expect(parseSub('acme/alice')).toEqual({ org: 'acme', username: 'alice' });
|
|
});
|
|
|
|
it('treats a value with no slash as a bare username', () => {
|
|
expect(parseSub('alice')).toEqual({ username: 'alice' });
|
|
});
|
|
|
|
it('splits on the first slash without leaking the separator', () => {
|
|
expect(parseSub('acme/')).toEqual({ org: 'acme' });
|
|
expect(parseSub('/alice')).toEqual({ username: 'alice' });
|
|
expect(parseSub('acme/team/alice')).toEqual({ org: 'acme', username: 'team/alice' });
|
|
});
|
|
|
|
it('returns empty for undefined', () => {
|
|
expect(parseSub(undefined)).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe('identityFromClaims', () => {
|
|
it('builds a signed-in identity from org/username + email', () => {
|
|
expect(identityFromClaims({ sub: 'acme/bob', email: '[email protected]' })).toEqual({
|
|
signedIn: true,
|
|
org: 'acme',
|
|
username: 'bob',
|
|
email: '[email protected]',
|
|
});
|
|
});
|
|
|
|
it('omits absent fields', () => {
|
|
expect(identityFromClaims({ sub: 'solo' })).toEqual({
|
|
signedIn: true,
|
|
username: 'solo',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('tenantIdentity / anonymous', () => {
|
|
it('is signed in when a tenant key exists', () => {
|
|
expect(tenantIdentity(true)).toEqual({ signedIn: true });
|
|
});
|
|
it('is signed out with no key', () => {
|
|
expect(tenantIdentity(false)).toEqual({ signedIn: false });
|
|
expect(anonymous()).toEqual({ signedIn: false });
|
|
});
|
|
});
|