fmt(js): npm run fix on merge (#86751)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0904f50e3e
commit
0807673e1f
@@ -9,8 +9,7 @@ import assert from 'node:assert/strict'
|
||||
|
||||
import { test } from 'vitest'
|
||||
|
||||
import type {
|
||||
ConnectionRegistry} from './connection-registry';
|
||||
import type { ConnectionRegistry } from './connection-registry'
|
||||
import {
|
||||
agentHandle,
|
||||
connectionIdForLabel,
|
||||
@@ -75,7 +74,8 @@ test('uniqueLabel counts up (never "X 2 2") and clamps long candidates', () => {
|
||||
|
||||
test('save rejects the reserved "local" id on non-local kinds', () => {
|
||||
assert.throws(
|
||||
() => normalizeConnectionInput({ id: 'local', kind: 'remote', label: 'Sneaky', url: 'http://x:1' }, emptyRegistry()),
|
||||
() =>
|
||||
normalizeConnectionInput({ id: 'local', kind: 'remote', label: 'Sneaky', url: 'http://x:1' }, emptyRegistry()),
|
||||
/reserved/
|
||||
)
|
||||
})
|
||||
@@ -108,7 +108,14 @@ test('token only persists on token-auth remotes; oauth/cloud drop it', () => {
|
||||
// --- mergeConnectionInput (edit inheritance) ---
|
||||
|
||||
test('merge preserves fields the editor does not carry (org, ssh extras)', () => {
|
||||
const cloud = { authMode: 'oauth' as const, id: 'c', kind: 'cloud' as const, label: 'Cloud', org: 'nous', url: 'https://a.cloud' }
|
||||
const cloud = {
|
||||
authMode: 'oauth' as const,
|
||||
id: 'c',
|
||||
kind: 'cloud' as const,
|
||||
label: 'Cloud',
|
||||
org: 'nous',
|
||||
url: 'https://a.cloud'
|
||||
}
|
||||
const renamed = mergeConnectionInput({ id: 'c', kind: 'cloud', label: 'Renamed', url: 'https://a.cloud' }, cloud)
|
||||
|
||||
assert.equal(renamed.org, 'nous')
|
||||
@@ -262,8 +269,22 @@ test('normalizeRegistry round-trips a valid registry unchanged in shape', () =>
|
||||
primary: 'homelab',
|
||||
connections: [
|
||||
{ id: 'local', kind: 'local', label: 'This device' },
|
||||
{ id: 'homelab', kind: 'remote', label: 'Homelab', url: 'http://10.0.0.5:9119', authMode: 'token', token: { v: 1 } },
|
||||
{ id: 'cloud-1', kind: 'cloud', label: 'Hermes Cloud', url: 'https://a.hermes.cloud', authMode: 'oauth', org: 'nous' },
|
||||
{
|
||||
id: 'homelab',
|
||||
kind: 'remote',
|
||||
label: 'Homelab',
|
||||
url: 'http://10.0.0.5:9119',
|
||||
authMode: 'token',
|
||||
token: { v: 1 }
|
||||
},
|
||||
{
|
||||
id: 'cloud-1',
|
||||
kind: 'cloud',
|
||||
label: 'Hermes Cloud',
|
||||
url: 'https://a.hermes.cloud',
|
||||
authMode: 'oauth',
|
||||
org: 'nous'
|
||||
},
|
||||
{ id: 'spark', kind: 'ssh', label: 'Spark', host: 'spark1', user: 'tek', port: 2222 }
|
||||
]
|
||||
}
|
||||
@@ -272,7 +293,10 @@ test('normalizeRegistry round-trips a valid registry unchanged in shape', () =>
|
||||
|
||||
assert.equal(registry.primary, 'homelab')
|
||||
assert.equal(registry.connections.length, 4)
|
||||
assert.deepEqual(registry.connections.map(c => c.id), ['local', 'homelab', 'cloud-1', 'spark'])
|
||||
assert.deepEqual(
|
||||
registry.connections.map(c => c.id),
|
||||
['local', 'homelab', 'cloud-1', 'spark']
|
||||
)
|
||||
assert.deepEqual(registry.connections[1].token, { v: 1 })
|
||||
assert.equal(registry.connections[3].port, 2222)
|
||||
})
|
||||
|
||||
@@ -27,7 +27,13 @@
|
||||
* these into the IPC layer and owns file I/O + secret encryption.
|
||||
*/
|
||||
|
||||
import { hostLabelFromBaseUrl, modeIsRemoteLike, normalizeRemoteBaseUrl, normalizeSshConfig, normAuthMode } from './connection-config'
|
||||
import {
|
||||
hostLabelFromBaseUrl,
|
||||
modeIsRemoteLike,
|
||||
normalizeRemoteBaseUrl,
|
||||
normalizeSshConfig,
|
||||
normAuthMode
|
||||
} from './connection-config'
|
||||
|
||||
export const REGISTRY_VERSION = 2
|
||||
|
||||
@@ -204,7 +210,12 @@ export function normalizeConnectionInput(input: ConnectionInput, registry: Conne
|
||||
throw new Error('The id "local" is reserved for the local connection.')
|
||||
}
|
||||
|
||||
const id = input.id || connectionIdForLabel(label, registry.connections.map(c => c.id))
|
||||
const id =
|
||||
input.id ||
|
||||
connectionIdForLabel(
|
||||
label,
|
||||
registry.connections.map(c => c.id)
|
||||
)
|
||||
|
||||
if (kind === 'ssh') {
|
||||
const ssh = normalizeSshConfig({
|
||||
@@ -436,7 +447,10 @@ export function migrateV1ToRegistry(v1: unknown): ConnectionRegistry {
|
||||
)
|
||||
|
||||
const entry: RegistryConnection = {
|
||||
id: connectionIdForLabel(label, connections.map(c => c.id)),
|
||||
id: connectionIdForLabel(
|
||||
label,
|
||||
connections.map(c => c.id)
|
||||
),
|
||||
kind,
|
||||
label,
|
||||
url,
|
||||
@@ -473,12 +487,18 @@ export function migrateV1ToRegistry(v1: unknown): ConnectionRegistry {
|
||||
return existing
|
||||
}
|
||||
|
||||
const label = uniqueLabel(ssh.host, connections.map(c => c.label))
|
||||
const label = uniqueLabel(
|
||||
ssh.host,
|
||||
connections.map(c => c.label)
|
||||
)
|
||||
|
||||
const { mode: _mode, ...sshFields } = ssh
|
||||
|
||||
const entry: RegistryConnection = {
|
||||
id: connectionIdForLabel(label, connections.map(c => c.id)),
|
||||
id: connectionIdForLabel(
|
||||
label,
|
||||
connections.map(c => c.id)
|
||||
),
|
||||
kind: 'ssh',
|
||||
label,
|
||||
...sshFields
|
||||
|
||||
@@ -7248,6 +7248,7 @@ async function discoverCloudAgents(org?: string) {
|
||||
const err = new Error(
|
||||
'Your Hermes Cloud session has expired. Open Settings → Gateway and sign in again.'
|
||||
) as any
|
||||
|
||||
err.needsCloudLogin = true
|
||||
err.cause = error
|
||||
throw err
|
||||
|
||||
@@ -362,6 +362,7 @@ export function observeUpdaterHandoff(
|
||||
deps: ObserveUpdaterHandoffDeps = {}
|
||||
): Promise<UpdaterHandoffOutcome> {
|
||||
const setTimeoutFn = deps.setTimeoutFn ?? setTimeout
|
||||
|
||||
const clearTimeoutFn =
|
||||
deps.clearTimeoutFn ?? ((timer: unknown) => clearTimeout(timer as ReturnType<typeof setTimeout>))
|
||||
|
||||
|
||||
@@ -346,7 +346,11 @@ function CronJobSidebarRow({
|
||||
onClick={onTrigger}
|
||||
type="button"
|
||||
>
|
||||
{busy ? <GlyphSpinner ariaLabel={c.triggerNow} className="text-[0.75rem]" /> : <Codicon name="zap" size="0.75rem" />}
|
||||
{busy ? (
|
||||
<GlyphSpinner ariaLabel={c.triggerNow} className="text-[0.75rem]" />
|
||||
) : (
|
||||
<Codicon name="zap" size="0.75rem" />
|
||||
)}
|
||||
</button>
|
||||
</Tip>
|
||||
<Tip label={c.manage}>
|
||||
|
||||
@@ -161,7 +161,9 @@ export const VirtualSessionList: FC<VirtualSessionListProps> = ({
|
||||
)}
|
||||
ref={scrollerRef}
|
||||
>
|
||||
<div className="relative" style={{ height: `${totalSize}px` }}>{rows}</div>
|
||||
<div className="relative" style={{ height: `${totalSize}px` }}>
|
||||
{rows}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,9 +143,7 @@ describe('mutateAndRefreshCronJobs', () => {
|
||||
it('allows overlapping same-profile mutations to authoritatively refresh', async () => {
|
||||
const first = deferred<string>()
|
||||
const second = deferred<string>()
|
||||
getCronJobs
|
||||
.mockResolvedValueOnce([{ id: 'after-second' }])
|
||||
.mockResolvedValueOnce([{ id: 'after-both' }])
|
||||
getCronJobs.mockResolvedValueOnce([{ id: 'after-second' }]).mockResolvedValueOnce([{ id: 'after-both' }])
|
||||
|
||||
const firstResult = mutateAndRefreshCronJobs('work', () => first.promise)
|
||||
const secondResult = mutateAndRefreshCronJobs('work', () => second.promise)
|
||||
|
||||
@@ -18,10 +18,7 @@ export interface CronMutationRefreshResult<T> extends CronTriggerRefreshResult {
|
||||
value: T | null
|
||||
}
|
||||
|
||||
async function refreshForGeneration(
|
||||
profile: string,
|
||||
request: CronJobsRequest
|
||||
): Promise<CronTriggerRefreshResult> {
|
||||
async function refreshForGeneration(profile: string, request: CronJobsRequest): Promise<CronTriggerRefreshResult> {
|
||||
try {
|
||||
const jobs = await getCronJobs(profile)
|
||||
|
||||
@@ -90,9 +87,7 @@ export async function triggerAndRefreshCronJobs(
|
||||
jobId: string,
|
||||
profile: 'all' | string
|
||||
): Promise<CronTriggerRefreshResult> {
|
||||
const { value: _value, ...result } = await mutateAndRefreshCronJobs(profile, () =>
|
||||
triggerCronJob(jobId)
|
||||
)
|
||||
const { value: _value, ...result } = await mutateAndRefreshCronJobs(profile, () => triggerCronJob(jobId))
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,9 +541,7 @@ export function CronView({ onClose, onOpenSession, setStatusbarItemGroup: _setSt
|
||||
setDeleting(true)
|
||||
|
||||
try {
|
||||
const { refreshError, stale } = await mutateAndRefreshCronJobs(profile, () =>
|
||||
deleteCronJob(pendingDelete.id)
|
||||
)
|
||||
const { refreshError, stale } = await mutateAndRefreshCronJobs(profile, () => deleteCronJob(pendingDelete.id))
|
||||
|
||||
if (stale) {
|
||||
return
|
||||
@@ -564,7 +562,11 @@ export function CronView({ onClose, onOpenSession, setStatusbarItemGroup: _setSt
|
||||
|
||||
async function handleEditorSave(values: EditorValues) {
|
||||
if (editor.mode === 'create') {
|
||||
const { value: created, refreshError, stale } = await mutateAndRefreshCronJobs(profile, () =>
|
||||
const {
|
||||
value: created,
|
||||
refreshError,
|
||||
stale
|
||||
} = await mutateAndRefreshCronJobs(profile, () =>
|
||||
createCronJob({
|
||||
prompt: values.prompt,
|
||||
schedule: values.schedule,
|
||||
@@ -586,7 +588,11 @@ export function CronView({ onClose, onOpenSession, setStatusbarItemGroup: _setSt
|
||||
} else if (editor.mode === 'edit') {
|
||||
const scriptOnlyJob = jobIsScriptOnly(editor.job)
|
||||
|
||||
const { value: updated, refreshError, stale } = await mutateAndRefreshCronJobs(profile, () =>
|
||||
const {
|
||||
value: updated,
|
||||
refreshError,
|
||||
stale
|
||||
} = await mutateAndRefreshCronJobs(profile, () =>
|
||||
updateCronJob(editor.job.id, cronEditorUpdates(values, { scriptOnlyJob }))
|
||||
)
|
||||
|
||||
@@ -612,7 +618,11 @@ export function CronView({ onClose, onOpenSession, setStatusbarItemGroup: _setSt
|
||||
async function handleBlueprintCreate(blueprint: AutomationBlueprint, values: Record<string, string>) {
|
||||
const writableProfile = profileScope === ALL_PROFILES ? 'default' : profileScope
|
||||
|
||||
const { value: job, refreshError, stale } = await mutateAndRefreshCronJobs(profile, () =>
|
||||
const {
|
||||
value: job,
|
||||
refreshError,
|
||||
stale
|
||||
} = await mutateAndRefreshCronJobs(profile, () =>
|
||||
instantiateAutomationBlueprint({ blueprint: blueprint.key, values }, writableProfile)
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@ import { translateNow } from '@/i18n'
|
||||
import { type GatewayEventPayload, textPart } from '@/lib/chat-messages'
|
||||
import { coerceGatewayText, coerceThinkingText, normalizePersonalityValue } from '@/lib/chat-runtime'
|
||||
import { playCompletionSound } from '@/lib/completion-sound'
|
||||
import { approvalReplaySessionId, resolveGatewayEventSessionId, UNSCOPED_STREAM_EVENT_TYPES } from '@/lib/gateway-events'
|
||||
import {
|
||||
approvalReplaySessionId,
|
||||
resolveGatewayEventSessionId,
|
||||
UNSCOPED_STREAM_EVENT_TYPES
|
||||
} from '@/lib/gateway-events'
|
||||
import { triggerHaptic } from '@/lib/haptics'
|
||||
import { modelOptionsQueryKey } from '@/lib/model-options'
|
||||
import { isProviderSetupErrorMessage } from '@/lib/provider-setup-errors'
|
||||
|
||||
@@ -577,7 +577,11 @@ const ThreadMessageListInner: FC<ThreadMessageListProps> = ({
|
||||
// can be overwritten by another mounted pane; leave a scrolled-up reader
|
||||
// exactly where they were.
|
||||
useEffect(
|
||||
() => subscribeToThreadForeground(() => isAtBottom, () => void scrollToBottom()),
|
||||
() =>
|
||||
subscribeToThreadForeground(
|
||||
() => isAtBottom,
|
||||
() => void scrollToBottom()
|
||||
),
|
||||
[isAtBottom, scrollToBottom]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
$cronJobs,
|
||||
beginCronJobsRequest,
|
||||
commitCronJobsRequest,
|
||||
setCronJobs,
|
||||
updateCronJobs
|
||||
} from './cron'
|
||||
import { $cronJobs, beginCronJobsRequest, commitCronJobsRequest, setCronJobs, updateCronJobs } from './cron'
|
||||
|
||||
const oldJob = { id: 'old' } as never
|
||||
const newJob = { id: 'new' } as never
|
||||
|
||||
@@ -91,12 +91,12 @@ describe('skillTouchedInMessages', () => {
|
||||
})
|
||||
|
||||
it('matches qualified skill names (category/name, plugin:name)', () => {
|
||||
expect(skillTouchedInMessages('hermes-agent-dev', [toolCall('skill_view', { name: 'github/hermes-agent-dev' })])).toBe(
|
||||
true
|
||||
)
|
||||
expect(skillTouchedInMessages('writing-plans', [toolCall('skill_view', { name: 'superpowers:writing-plans' })])).toBe(
|
||||
true
|
||||
)
|
||||
expect(
|
||||
skillTouchedInMessages('hermes-agent-dev', [toolCall('skill_view', { name: 'github/hermes-agent-dev' })])
|
||||
).toBe(true)
|
||||
expect(
|
||||
skillTouchedInMessages('writing-plans', [toolCall('skill_view', { name: 'superpowers:writing-plans' })])
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('falls back to argsText when args were not parsed', () => {
|
||||
|
||||
@@ -149,7 +149,11 @@ export function skillTouchedInMessages(skillName: string, messages: readonly Cha
|
||||
|
||||
for (const message of messages) {
|
||||
for (const part of message.parts) {
|
||||
if (part.type === 'tool-call' && SKILL_TOOL_NAMES.has(part.toolName) && argNamesSkill(skillArgName(part), skillName)) {
|
||||
if (
|
||||
part.type === 'tool-call' &&
|
||||
SKILL_TOOL_NAMES.has(part.toolName) &&
|
||||
argNamesSkill(skillArgName(part), skillName)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ function workspaceManifests(): { name: string, manifest: Manifest }[] {
|
||||
for (const pattern of patterns) {
|
||||
// The globs in use are plain paths or a single trailing ``/*``.
|
||||
const parent = pattern.endsWith('/*') ? path.join(REPO_ROOT, pattern.slice(0, -2)) : null
|
||||
|
||||
const dirs = parent === null
|
||||
? [pattern]
|
||||
: fs.existsSync(parent)
|
||||
@@ -78,6 +79,7 @@ test('workspaces declaring react and react-dom pin them to the same exact versio
|
||||
|
||||
if (react !== reactDom) {
|
||||
offenders.push(`${name} declares react"${react}" but react-dom"${reactDom}"`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user