feat!: move to the alpha track — DSH 0.1.2-alpha.x only (v0.18.0-alpha.0)
DSH itself is still on its 0.1.2-alpha line (npm `alpha` dist-tag), so the
plugin mirrors it: v0.18.0-alpha.0 publishes under the npm `alpha`
dist-tag and supports DSH 0.1.2-alpha.x only. Stable-DSH (0.1.0-rc.8 ~
0.1.1-rc.2) users stay on v0.17.1 (npm `latest`). Verified end-to-end
against a real 0.1.2-alpha.2 host: full vitest suite, typecheck,
build/pack, consumer type surface, and the headless mount smoke 14/14
(the prerelease version also passes `dsh plugin add file:`).
- peer floor raised `^0.1.0-rc.8` -> `^0.1.2-alpha.2` (the mount install
no longer warns: the old floor could not semver-match a prerelease
host); dsh.plugin.json engines.dsh `>=0.1.2-alpha.2`.
- Dropped the pre-alpha compatibility layer:
- e2e host RPC collapsed from the dot/slash dual dialect to slash-only
(rpcAttempt; the 404-probe negotiation and its cache are gone), and
parseLaunchUrl now requires the ?token= launch URL;
- MarkdownText labels collapsed to the nested single shape (the cast
became unnecessary against the alpha.2 declarations);
- the defunct `@deepseek-ai/dsh-client-runtime` row is gone from the
chunk externals allowlist (chunk-loader / tsdown / two specs) and
from dsh.client.inject.
- market-manifest guard now allows an exact prerelease version (alpha
cuts are not the market's verified `latest` target); release.yml picks
the npm dist-tag from the version (prerelease -> `alpha`, else
`latest`) so an alpha can never hijack `latest`.
- Docs retargeted: AGENTS.md §2/§3, README/README_EN badges + v0.18.0
-alpha.0 section + install note (`dsh-better-sidebar@alpha`), guide.
This commit is contained in:
+49
-57
@@ -1,28 +1,28 @@
|
||||
/**
|
||||
* Pure host-transport contracts for the e2e lanes, spanning the DSH
|
||||
* 0.1.1-rc.x → 0.1.2-alpha.1 breaking split (Remote gateway + one-time-token
|
||||
* browser auth). No Playwright imports here — these shapes are unit-testable
|
||||
* Pure host-transport contracts for the e2e lanes against the DSH
|
||||
* 0.1.2-alpha.x web host (Remote gateway + one-time-token browser auth). No
|
||||
* Playwright imports here — these shapes are unit-testable
|
||||
* (tests/e2e-host-protocol.spec.ts); ./host.ts wires them to a request
|
||||
* context.
|
||||
*
|
||||
* What changed in 0.1.2-alpha.1 (deepseek-harness tag dsh-v0.1.2-alpha.1):
|
||||
* Host contract (deepseek-harness tags dsh-v0.1.2-alpha.1/2, verified on
|
||||
* live hosts; the pre-alpha dual-dialect layer was dropped in the plugin's
|
||||
* v0.18.0-alpha.0 alpha track):
|
||||
*
|
||||
* - `dsh web` prints an AUTHENTICATED launch URL — `dsh web:
|
||||
* http://127.0.0.1:<port>/?token=<43 base64url chars>`. Navigating it
|
||||
* exchanges the token for a signed cookie (HttpOnly, SameSite=Strict) that
|
||||
* every `/api` request must then carry; the clean URL answers 401. Older
|
||||
* hosts print a bare origin and have no browser auth.
|
||||
* - The legacy ApiProxy dot-method endpoints (`POST /api/workspace.create`
|
||||
* with `payload: <args>`) were replaced by the Remote gateway's slash
|
||||
* endpoints — `POST /api/workspace/create` with `payload: { args }` whose
|
||||
* args object is keyed by the controller's TypeScript PARAMETER name
|
||||
* (`workspace/create` → `{ request: {...} }`; `session/list`'s unused
|
||||
* parameter is literally `_request` and is not omissible); a dot path is
|
||||
* no longer claimed (404). The `{type:'client-request', rpcId, method,
|
||||
* payload}` / `{type:'server-response', rpcId, result}` envelopes are
|
||||
* unchanged, and the plugin's own `/sidebar/*` routes stay public (the
|
||||
* webserver carrier owns no authentication — only `/api`, the index HTML,
|
||||
* and the remote mux upgrade sit behind the browser auth).
|
||||
* every `/api` request must then carry; the clean URL answers 401.
|
||||
* - The Remote gateway serves SLASH endpoints — `POST /api/workspace/create`
|
||||
* with `payload: { args }` whose args object is keyed by the controller's
|
||||
* TypeScript PARAMETER name (`workspace/create` → `{ request: {...} }`;
|
||||
* `session/list`'s unused parameter is literally `_request` and is NOT
|
||||
* omissible); the legacy dot path is no longer claimed (404). The
|
||||
* `{type:'client-request', rpcId, method, payload}` /
|
||||
* `{type:'server-response', rpcId, result}` envelopes are unchanged, and
|
||||
* the plugin's own `/sidebar/*` routes stay public (the webserver carrier
|
||||
* owns no authentication — only `/api`, the index HTML, and the remote mux
|
||||
* upgrade sit behind the browser auth).
|
||||
*/
|
||||
|
||||
/** The pieces of a `dsh web` launch URL the lanes need. */
|
||||
@@ -30,22 +30,23 @@ export interface LaunchUrl {
|
||||
/** Scheme + host + port — the base for URL construction (a token URL would
|
||||
* corrupt path concatenation). */
|
||||
origin: string
|
||||
/** The URL as printed (token query included on 0.1.2-alpha.1+); the correct
|
||||
* page.goto target — navigating it performs the token→cookie exchange. */
|
||||
/** The URL as printed (token query included) — the correct page.goto
|
||||
* target; navigating it performs the token→cookie exchange. */
|
||||
pageUrl: string
|
||||
/** The one-time launch token, when the host printed one. */
|
||||
token: string | undefined
|
||||
/** The one-time launch token. */
|
||||
token: string
|
||||
}
|
||||
|
||||
/** Split a `dsh web` launch URL (bare origin on 0.1.1-rc.x, `/?token=` on
|
||||
* 0.1.2-alpha.1+) into the pieces the lanes address. */
|
||||
/** Split a `dsh web` launch URL (`/?token=` — every supported host prints
|
||||
* one) into the pieces the lanes address. Throws without a token: a bare
|
||||
* origin is a pre-0.1.2-alpha host this plugin no longer supports. */
|
||||
export function parseLaunchUrl(raw: string): LaunchUrl {
|
||||
const url = new URL(raw)
|
||||
return {
|
||||
origin: url.origin,
|
||||
pageUrl: raw,
|
||||
token: url.searchParams.get('token') ?? undefined,
|
||||
const token = url.searchParams.get('token')
|
||||
if (token === null) {
|
||||
throw new Error(`launch URL carries no ?token= — not a DSH 0.1.2-alpha+ authenticated launch URL: ${raw}`)
|
||||
}
|
||||
return { origin: url.origin, pageUrl: raw, token }
|
||||
}
|
||||
|
||||
/** Page navigation URL with extra query stamps (e.g. desktop-shell URL
|
||||
@@ -57,48 +58,39 @@ export function pageUrlWith(raw: string, extra: Record<string, string>): string
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
/** One host-RPC protocol attempt. */
|
||||
/** One host-RPC request. */
|
||||
export interface RpcAttempt {
|
||||
/** Which host dialect this attempt speaks. */
|
||||
protocol: 'dot' | 'slash'
|
||||
/** Request path, relative to the origin, starting with `/api/`. */
|
||||
path: string
|
||||
/** Envelope `method` string — must equal the endpoint the path selects. */
|
||||
method: string
|
||||
/** Envelope `payload` — the bare args object on dot hosts, `{ args }` on
|
||||
* slash hosts (the gateway rejects any other payload shape). */
|
||||
/** Envelope `payload` — `{ args }` keyed by the controller parameter name
|
||||
* (the gateway rejects any other payload shape). */
|
||||
payload: Record<string, unknown>
|
||||
}
|
||||
|
||||
/** The slash-dialect args key per host method: the 0.1.2-alpha.1 Remote
|
||||
* controllers key the args object by the TypeScript PARAMETER name, not by
|
||||
* the endpoint's own naming — workspace/create and session/create declare a
|
||||
* single `request` parameter, while session/list's unused parameter is
|
||||
* literally named `_request` (and is NOT omissible: the typert gateway
|
||||
* rejects `{}` and `{request:{}}` alike). Verified against a live
|
||||
* dsh-v0.1.2-alpha.1 host: every other shape fails with `args fields do not
|
||||
* match the descriptor`. */
|
||||
const SLASH_ARGS_KEY: Record<string, string> = {
|
||||
/** The args key per host method: the 0.1.2-alpha Remote controllers key the
|
||||
* args object by the TypeScript PARAMETER name, not by the endpoint's own
|
||||
* naming — workspace/create and session/create declare a single `request`
|
||||
* parameter, while session/list's unused parameter is literally named
|
||||
* `_request` (and is NOT omissible: the typert gateway rejects `{}` and
|
||||
* `{request:{}}` alike). Verified against live dsh-v0.1.2-alpha hosts:
|
||||
* every other shape fails with `args fields do not match the descriptor`. */
|
||||
const RPC_ARGS_KEY: Record<string, string> = {
|
||||
'workspace.create': 'request',
|
||||
'session.create': 'request',
|
||||
'session.list': '_request',
|
||||
}
|
||||
|
||||
/** Build the two dialects of one host RPC (method in dot form, e.g.
|
||||
* `'workspace.create'`): the 0.1.1-rc.x dot endpoint first — the dialect
|
||||
* every currently-published host speaks, so the deployed lanes never pay a
|
||||
* probe round-trip — then the 0.1.2-alpha.1+ slash endpoint as the 404
|
||||
* fallback. Throws for methods without a known slash args key: the wrapper
|
||||
* is per-parameter, so an unverified shape must fail loudly at build time
|
||||
* instead of as a confusing gateway error mid-lane. */
|
||||
export function rpcAttempts(method: string, args: Record<string, unknown>): RpcAttempt[] {
|
||||
const slashKey = SLASH_ARGS_KEY[method]
|
||||
if (slashKey === undefined) {
|
||||
throw new Error(`host-protocol: no slash-dialect args key for ${JSON.stringify(method)} — verify the parameter name on the host and extend SLASH_ARGS_KEY`)
|
||||
/** Build one host RPC request (method in dot form, e.g.
|
||||
* `'workspace.create'`). Throws for methods without a known args key: the
|
||||
* wrapper is per-parameter, so an unverified shape must fail loudly at
|
||||
* build time instead of as a confusing gateway error mid-lane. */
|
||||
export function rpcAttempt(method: string, args: Record<string, unknown>): RpcAttempt {
|
||||
const key = RPC_ARGS_KEY[method]
|
||||
if (key === undefined) {
|
||||
throw new Error(`host-protocol: no args key for ${JSON.stringify(method)} — verify the parameter name on the host and extend RPC_ARGS_KEY`)
|
||||
}
|
||||
const slash = method.split('.').join('/')
|
||||
return [
|
||||
{ protocol: 'dot', path: `/api/${method}`, method, payload: args },
|
||||
{ protocol: 'slash', path: `/api/${slash}`, method: slash, payload: { args: { [slashKey]: args } } },
|
||||
]
|
||||
const endpoint = method.split('.').join('/')
|
||||
return { path: `/api/${endpoint}`, method: endpoint, payload: { args: { [key]: args } } }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user