feat: integrate fork work onto upstream main (squashed)

Squash-merge of feat/autofix-pr-test (69 commits) onto upstream/main
with -X ours strategy (upstream as authoritative for content conflicts).

Key features brought in from fork:
- LocalMemoryRecall + VaultHttpFetch tools (end-to-end wired)
- /local-memory, /local-vault, /memory-stores, /skill-store interactive panels
- /agents-platform, /schedule, /vault command scaffolding
- /login: switch / replace / remove of workspace API key
- statusline refactor (built-in status row, /statusline as info command)
- autofix-pr command + workflow

Conflict resolutions (upstream-wins):
- 10 .js command stubs kept from upstream (alongside fork's .ts implementations)
- src/components/BuiltinStatusLine.tsx accepted upstream's deletion
  (fork's wire-up references in StatusLine.tsx will be cleaned up next)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
unraid
2026-05-08 16:47:29 +08:00
parent 73e54d4bbc
commit 8945f08708
233 changed files with 40597 additions and 341 deletions

View File

@@ -0,0 +1,192 @@
/**
* Regression tests for launchCommand factory (H2 finding).
* Tests MUST fail before the factory is created, then pass after.
*/
import { describe, test, expect, mock } from 'bun:test'
import { logMock } from '../../../../tests/mocks/log.js'
mock.module('src/utils/log.ts', logMock)
mock.module('bun:bundle', () => ({ feature: () => false }))
import React from 'react'
import type {
LocalJSXCommandCall,
LocalJSXCommandOnDone,
} from '../../../types/command.js'
import type { LaunchCommandOptions } from '../launchCommand.js'
let launchCommand: typeof import('../launchCommand.js').launchCommand
// Lazy import so mocks are in place first
const loadModule = async () => {
const mod = await import('../launchCommand.js')
launchCommand = mod.launchCommand
}
// Simple parsed union for tests
type TestParsed =
| { action: 'greet'; name: string }
| { action: 'invalid'; reason: string }
type TestViewProps = { greeting: string }
const TestView: React.FC<TestViewProps> = ({ greeting }) =>
React.createElement('span', null, greeting)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyOpts = LaunchCommandOptions<any, any>
const makeOpts = (overrides: Partial<AnyOpts> = {}): AnyOpts => ({
commandName: 'test-cmd',
parseArgs: (
raw: string,
): TestParsed | { action: 'invalid'; reason: string } => {
if (raw.trim() === '') return { action: 'invalid', reason: 'empty args' }
return { action: 'greet', name: raw.trim() }
},
dispatch: async (parsed: TestParsed, onDone: LocalJSXCommandOnDone) => {
if (parsed.action !== 'greet') return null
onDone(`Hello ${parsed.name}`)
return { greeting: `Hello, ${parsed.name}!` }
},
View: TestView as React.FC<unknown>,
errorView: (msg: string) =>
React.createElement('span', null, `Error: ${msg}`),
...overrides,
})
describe('launchCommand factory', () => {
test('module loads and exports launchCommand function', async () => {
await loadModule()
expect(typeof launchCommand).toBe('function')
})
test('launchCommand returns a LocalJSXCommandCall function', async () => {
await loadModule()
const call = launchCommand(makeOpts())
expect(typeof call).toBe('function')
})
test('happy path: parseArgs + dispatch succeed → View rendered, onDone called', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(makeOpts())
const onDone = mock(() => {})
const result = await call(onDone, {} as never, 'Alice')
expect(result).not.toBeNull()
expect(onDone).toHaveBeenCalledTimes(1)
const [msg] = onDone.mock.calls[0] as unknown as [string]
expect(msg).toContain('Alice')
})
test('parseArgs returns invalid → errorView returned, onDone called with reason', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(makeOpts())
const onDone = mock(() => {})
const result = await call(onDone, {} as never, '')
expect(onDone).toHaveBeenCalledTimes(1)
const [msg] = onDone.mock.calls[0] as unknown as [string]
expect(msg).toContain('empty args')
// errorView should return something (not null from dispatch)
expect(result).not.toBeUndefined()
})
test('dispatch throws → errorView returned, onDone called with error message', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(
makeOpts({
dispatch: async () => {
throw new Error('dispatch failed')
},
}),
)
const onDone = mock(() => {})
const result = await call(onDone, {} as never, 'Bob')
expect(onDone).toHaveBeenCalledTimes(1)
const [msg] = onDone.mock.calls[0] as unknown as [string]
expect(msg).toContain('dispatch failed')
expect(result).not.toBeUndefined()
})
test('dispatch returns null → null returned from call', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(
makeOpts({
dispatch: async (_parsed, onDone) => {
onDone('done')
return null
},
}),
)
const onDone = mock(() => {})
const result = await call(onDone, {} as never, 'Charlie')
expect(result).toBeNull()
})
test('onDispatchError hook is called when dispatch throws', async () => {
await loadModule()
const onDispatchError = mock((_err: unknown) => {})
const call: LocalJSXCommandCall = launchCommand(
makeOpts({
dispatch: async () => {
throw new Error('boom')
},
onDispatchError,
}),
)
const onDone = mock(() => {})
await call(onDone, {} as never, 'Dave')
expect(onDispatchError).toHaveBeenCalledTimes(1)
})
test('invalid args: onDone display option is system', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(makeOpts())
const capturedOpts: unknown[] = []
const onDone = mock((_msg?: string, opts?: unknown) => {
capturedOpts.push(opts)
})
await call(onDone, {} as never, '')
expect(capturedOpts[0]).toEqual({ display: 'system' })
})
test('dispatch error: onDone is called exactly once with commandName in message', async () => {
await loadModule()
const call: LocalJSXCommandCall = launchCommand(
makeOpts({
commandName: 'my-special-cmd',
dispatch: async () => {
throw new Error('network timeout')
},
}),
)
const onDone = mock(() => {})
await call(onDone, {} as never, 'Eve')
expect(onDone).toHaveBeenCalledTimes(1)
const [msg] = onDone.mock.calls[0] as unknown as [string]
expect(msg).toContain('my-special-cmd')
expect(msg).toContain('network timeout')
})
test('errorView receives the error message string', async () => {
await loadModule()
const capturedMsgs: string[] = []
const call: LocalJSXCommandCall = launchCommand(
makeOpts({
dispatch: async () => {
throw new Error('specific-error-text')
},
errorView: (msg: string) => {
capturedMsgs.push(msg)
return React.createElement('span', null, msg)
},
}),
)
await call(
mock(() => {}),
{} as never,
'Frank',
)
expect(capturedMsgs).toHaveLength(1)
expect(capturedMsgs[0]).toBe('specific-error-text')
})
})

View File

@@ -0,0 +1,122 @@
/**
* launchCommand — generic factory for local-jsx command implementations.
*
* Encapsulates the repeated boilerplate across the 6 command launch files:
* - args parsing + invalid-args handling
* - dispatch error capture + onDone error message
* - errorView rendering
* - React.createElement call for the happy-path View
*
* Usage (H2 finding — cuts boilerplate ~50%):
*
* export const callMyCmd: LocalJSXCommandCall = launchCommand<MyParsed, MyViewProps>({
* commandName: 'my-cmd',
* parseArgs: parseMyArgs,
* dispatch: async (parsed, onDone, context) => { ... return viewProps },
* View: MyCmdView,
* errorView: (msg) => React.createElement(MyCmdView, { mode: 'error', message: msg }),
* })
*/
import React from 'react'
import type {
LocalJSXCommandCall,
LocalJSXCommandOnDone,
} from '../../types/command.js'
import type { ToolUseContext } from '../../Tool.js'
/** Shape returned by parseArgs when args are invalid. */
export interface InvalidParsed {
action: 'invalid'
reason: string
}
export interface LaunchCommandOptions<TParsed, TViewProps> {
/**
* Command name used in error messages (e.g. "local-vault").
* Appears in the onDone text when dispatch throws.
*/
commandName: string
/**
* Parse raw args string into a typed action union or an invalid sentinel.
* Must return `{ action: 'invalid'; reason: string }` when args are bad.
*/
parseArgs: (rawArgs: string) => TParsed | InvalidParsed
/**
* Perform the command operation.
* - Call onDone with the user-visible summary text.
* - Return the View props to render, or null to render nothing.
* - Throw to trigger the error path.
*/
dispatch: (
parsed: TParsed,
onDone: LocalJSXCommandOnDone,
context: ToolUseContext,
) => Promise<TViewProps | null>
/**
* React component rendered with the props returned by dispatch.
*/
View: React.FC<TViewProps>
/**
* Render an error node when parseArgs returns invalid or dispatch throws.
* Receives the human-readable error message string.
*/
errorView: (message: string) => React.ReactNode
/**
* Optional hook called when dispatch throws, before the error is surfaced.
* Useful for analytics logEvent calls.
* Default: no-op.
*/
onDispatchError?: (err: unknown) => void
}
/**
* Returns a LocalJSXCommandCall that wraps the provided parse / dispatch / View
* triple with uniform error handling.
*/
export function launchCommand<TParsed, TViewProps>(
opts: LaunchCommandOptions<TParsed, TViewProps>,
): LocalJSXCommandCall {
return async (
onDone: LocalJSXCommandOnDone,
context: ToolUseContext,
args: string,
): Promise<React.ReactNode> => {
// ── Parse args ────────────────────────────────────────────────────────────
const parsed = opts.parseArgs(args ?? '')
if (isInvalid(parsed)) {
onDone(`Invalid args: ${parsed.reason}`, { display: 'system' })
return opts.errorView(parsed.reason)
}
// ── Dispatch ──────────────────────────────────────────────────────────────
try {
const viewProps = await opts.dispatch(parsed as TParsed, onDone, context)
if (viewProps === null) return null
return React.createElement(
opts.View as React.ComponentType<object>,
viewProps as object,
)
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err)
opts.onDispatchError?.(err)
onDone(`${opts.commandName} failed: ${msg}`, { display: 'system' })
return opts.errorView(msg)
}
}
}
function isInvalid(parsed: unknown): parsed is InvalidParsed {
return (
typeof parsed === 'object' &&
parsed !== null &&
'action' in parsed &&
(parsed as InvalidParsed).action === 'invalid'
)
}