mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
* feat: windmill-chat sdk for chat-mode flows in external frontends and raw apps Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aQiZNAU8g17kWkyTryS5J * fix: keep streamed answers until persisted, finish turns after history fallback Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aQiZNAU8g17kWkyTryS5J * feat: ai sdk transport and assistant-ui runtime for windmill-chat Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: finish a turn from the flow result until its answer row lands, hash chat ids without crypto.subtle Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: judge a turn answered by a persisted assistant row, wherever it was fetched Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: attribute a turn's answer to its own jobs, keep a local turn when switching conversations Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: mirror local history on every change, attribute failure-handler answers to the turn Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: new chat per token string in the React hook, idle after destroy, no reorder on view Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: recreate the hook's chat on any credential change, namespace local history per user Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix: send the latest inputs from the React hook Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
50 lines
2.1 KiB
TypeScript
50 lines
2.1 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test'
|
|
import { resolveConfig } from '../src/config'
|
|
|
|
const g = globalThis as { process?: unknown; ctx?: unknown; location?: unknown }
|
|
const originalProcess = g.process
|
|
|
|
afterEach(() => {
|
|
g.process = originalProcess
|
|
delete g.ctx
|
|
delete g.location
|
|
})
|
|
|
|
describe('resolveConfig', () => {
|
|
test('explicit token defaults history to local; a session defaults to server', () => {
|
|
const base = { flowPath: 'f/a/b', baseUrl: 'http://wm.test/', workspace: 'ws' }
|
|
expect(resolveConfig({ ...base, token: 't' }).history).toBe('local')
|
|
expect(resolveConfig(base).history).toBe('server')
|
|
expect(resolveConfig({ ...base, token: 't', history: 'server' }).historyExplicit).toBe(true)
|
|
})
|
|
|
|
test('reads the sandboxed raw app env the wrapper injects', () => {
|
|
g.process = {
|
|
env: { WM_RAW_APP: 'true', WM_TOKEN: 'sdk-token', BASE_URL: 'http://wm.test', WM_WORKSPACE: 'ws' }
|
|
}
|
|
const config = resolveConfig({ flowPath: 'f/a/b' })
|
|
expect(config).toMatchObject({ baseUrl: 'http://wm.test', workspace: 'ws', token: 'sdk-token', history: 'server' })
|
|
})
|
|
|
|
test('keeps the raw app token off another instance', () => {
|
|
g.process = {
|
|
env: { WM_RAW_APP: 'true', WM_TOKEN: 'sdk-token', BASE_URL: 'http://wm.test', WM_WORKSPACE: 'ws' }
|
|
}
|
|
expect(resolveConfig({ flowPath: 'f/a/b', baseUrl: 'http://other.test', workspace: 'ws' }).token).toBeUndefined()
|
|
expect(resolveConfig({ flowPath: 'f/a/b', baseUrl: 'http://wm.test' }).token).toBe('sdk-token')
|
|
})
|
|
|
|
test('reads the unsandboxed raw app context and uses the page origin', () => {
|
|
g.ctx = { ctx: { username: 'admin' }, workspace: 'ws' }
|
|
g.location = { origin: 'http://wm.test' }
|
|
const config = resolveConfig({ flowPath: 'f/a/b' })
|
|
expect(config).toMatchObject({ baseUrl: 'http://wm.test', workspace: 'ws', token: undefined })
|
|
})
|
|
|
|
test('refuses an opaque origin without an SDK token', () => {
|
|
g.ctx = { workspace: 'ws' }
|
|
g.location = { origin: 'null' }
|
|
expect(() => resolveConfig({ flowPath: 'f/a/b' })).toThrow('frontend SDK scopes')
|
|
})
|
|
})
|