refactor(client): use settings Remote namespaces

This commit is contained in:
imccyu
2026-08-27 03:01:02 +08:00
parent dd70c0c88d
commit 5918dd205e
65 changed files with 1103 additions and 819 deletions
@@ -141,7 +141,7 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
// Regression pin for the reload flash: both steps are satisfied, yet each
// must load private facts before deciding not to show. Dialog chrome lives
// inside each visible branch, so the deciding window paints and blocks
// nothing. Holding settings.describe widens that window from loopback
// nothing. Holding settings/describe widens that window from loopback
// RTT scale to a deterministic hundreds of milliseconds, removing all
// timing dependence from the sampler assertions below.
//
@@ -162,7 +162,7 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
if (document.getElementById('root')?.inert === true) sightings.push('inert')
}, 8)
})
// EVERY settings.describe issued before the release is held — not just
// EVERY settings/describe issued before the release is held — not just
// the first — so the pin cannot silently collapse back to loopback
// timing if a second boot-time consumer of the join ever appears.
let released = false
@@ -171,7 +171,7 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
released = true
for (const resolve of heldRoutes.splice(0)) resolve()
}
await page.route('**/api/settings.describe', async (route) => {
await page.route('**/api/settings/describe', async (route) => {
if (!released) await new Promise<void>((resolve) => { heldRoutes.push(resolve) })
await route.continue()
})
@@ -182,7 +182,7 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
await page.waitForTimeout(600)
releaseDescribe()
await page.waitForTimeout(400)
await page.unroute('**/api/settings.describe')
await page.unroute('**/api/settings/describe')
acknowledgeReloadConnectionLoss(tripwire, warningsBefore)
expect(await page.evaluate(() =>
(window as unknown as { __takeoverSightings: string[] }).__takeoverSightings)).toEqual([])
+35 -24
View File
@@ -317,17 +317,6 @@ async function bootPreview(origin: string, browser: Browser): Promise<void> {
type Result<T> = { result: { ok: true; value: T } | { ok: false; error: { code: string; message: string } } }
interface PreviewApi {
skills: { list(payload: { sessionId: string }): Promise<Result<{ skills: unknown[] }>> }
settings: {
describe(payload: object): Promise<Result<{ namespaces: Array<{ ns: string; revision: number }> }>>
update(payload: { ns: string; patch: object; expectedRevision: number }): Promise<Result<unknown>>
}
credentials: {
set(payload: { ref: string; value: string }): Promise<Result<unknown>>
unset(payload: { ref: string }): Promise<Result<unknown>>
describe(payload: { refs: string[] }): Promise<Result<{
credentials: Record<string, { configured: boolean }>
}>>
}
}
interface PreviewTransport {
fetch(input: string, init: RequestInit): Promise<Response>
@@ -373,22 +362,44 @@ async function bootPreview(origin: string, browser: Browser): Promise<void> {
if (!refreshed.result.ok) throw new Error(`skill.list refresh failed: ${refreshed.result.error.message}`)
}
await createDirectory('/dsh/workspace/.agents/skills', 'runtime-created')
const settings = await api.settings.describe({})
if (!settings.result.ok) throw new Error(`settings.describe failed: ${settings.result.error.message}`)
const shell = settings.result.value.namespaces.find(namespace => namespace.ns === 'shell')
if (shell === undefined) throw new Error('settings.describe omitted the shell namespace')
const updated = await api.settings.update({ ns: 'shell', patch: { timeoutMs: 61_000 }, expectedRevision: shell.revision })
if (!updated.result.ok) throw new Error(`settings.update failed: ${updated.result.error.message}`)
const stored = await api.credentials.set({ ref: 'PREVIEW_TEST_SECRET', value: 'worker-only' })
if (!stored.result.ok) throw new Error(`credentials.set failed: ${stored.result.error.message}`)
const credentials = await api.credentials.describe({ refs: ['PREVIEW_TEST_SECRET'] })
if (!credentials.result.ok) throw new Error(`credentials.describe failed: ${credentials.result.error.message}`)
const removed = await api.credentials.unset({ ref: 'PREVIEW_TEST_SECRET' })
if (!removed.result.ok) throw new Error(`credentials.unset failed: ${removed.result.error.message}`)
// Settings and credentials both answer over the Remote carrier, so this
// half of the sweep posts the generated endpoints directly like the
// session read above.
const remote = async <T>(endpoint: string, args: object): Promise<T> => {
const answered = await transport.fetch(`/api/${endpoint}`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
type: 'client-request',
rpcId: `preview-${endpoint.replace('/', '-')}`,
method: endpoint,
payload: { args },
}),
})
const body = await answered.json() as Result<T>
if (!body.result.ok) throw new Error(`${endpoint} failed: ${body.result.error.message}`)
return body.result.value
}
const settings = await remote<{ namespaces: { ns: string; revision: number }[] }>(
'settings/describe', {},
)
const shell = settings.namespaces.find(namespace => namespace.ns === 'shell')
if (shell === undefined) throw new Error('settings/describe omitted the shell namespace')
await remote('settings/update', {
ns: 'shell',
patch: { timeoutMs: 61_000 },
expectedRevision: shell.revision,
})
await remote('credentials/set', { ref: 'PREVIEW_TEST_SECRET', value: 'worker-only' })
const credentials = await remote<Record<string, { configured: boolean }>>(
'credentials/describe',
{ refs: ['PREVIEW_TEST_SECRET'] },
)
await remote('credentials/unset', { ref: 'PREVIEW_TEST_SECRET' })
await new Promise((resolve) => { setTimeout(resolve, 250) })
return {
skillCount: skills.result.value.skills.length,
credentialConfigured: credentials.result.value.credentials.PREVIEW_TEST_SECRET?.configured,
credentialConfigured: credentials.PREVIEW_TEST_SECRET?.configured,
}
})
expect(exercised.skillCount).toBeGreaterThan(0)
+2 -2
View File
@@ -1,4 +1,4 @@
// Cold boot may issue at most two settings.describe calls regardless of client
// Cold boot may issue at most two settings/describe calls regardless of client
// plugin count. No model call or replay fixture is involved.
import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright'
@@ -38,7 +38,7 @@ describe('startup RPC budget', () => {
// absorbs the first-connection reset wave the budget must include.
await page.getByRole('textbox', { name: 'Choose workspace' }).waitFor({ timeout: 30_000 })
await page.waitForTimeout(3000)
const describeCount = calls.filter(method => method === 'settings.describe').length
const describeCount = calls.filter(method => method === 'settings/describe').length
expect(describeCount, `startup /api calls:\n${calls.join('\n')}`).toBe(DESCRIBE_BUDGET)
})
})