|
|
@@ -1,12 +1,13 @@
|
|
|
/**
|
|
|
* Adaptive chooser of the directory-picker seam: resolves the host's
|
|
|
* situation once at boot (bind host, SSH launch, display session, Linux
|
|
|
- * chooser binary) and mounts the matching dual-face backend — `-native` or
|
|
|
- * `-browse` — as a real Loader entry in the in-memory root tree. Because the
|
|
|
- * backend arrives as an ordinary entry, its browser half is discovered
|
|
|
- * exactly as a config-row's would be, so the seam's one-row-swaps-both-faces
|
|
|
- * invariant holds for the resolved choice; pinning an interaction remains
|
|
|
- * composing that backend row directly instead of this one.
|
|
|
+ * chooser binary) and mounts the matching interaction — `native` or `browse`
|
|
|
+ * — as real Loader entries in the in-memory root tree. Each interaction is a
|
|
|
+ * pair: the Host backend serving the seam capability and the client surface
|
|
|
+ * occupying ui-workspace's directory-flow holes. Both arrive as ordinary
|
|
|
+ * entries, so the surface is discovered exactly as a config-row's would be
|
|
|
+ * and one resolved choice still swaps both faces; pinning an interaction
|
|
|
+ * remains composing that pair directly instead of this row.
|
|
|
* @module @deepseek-ai/dsh-host-directory-picker-auto
|
|
|
*/
|
|
|
|
|
|
@@ -28,7 +29,7 @@ export const name = 'directory-picker-auto'
|
|
|
export const inject = ['httpServer', 'loader']
|
|
|
|
|
|
/**
|
|
|
- * Backend package per resolved kind — fixed composition vocabulary, not a
|
|
|
+ * Host backend package per resolved kind — fixed composition vocabulary, not a
|
|
|
* tunable. Exported because the reference is a runtime string the static
|
|
|
* config gate cannot see in a yml row: `verify-cordis-config` requires every
|
|
|
* app composing this chooser to declare both values as dependencies.
|
|
|
@@ -39,10 +40,20 @@ export const BACKEND_PACKAGES: Record<DirectoryPickerBackendKind, string> = {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Resolve the backend from one boot-time sample and mount it as a Loader
|
|
|
- * entry; the effect's disposer removes the entry and joins the backend
|
|
|
- * fiber's teardown, so unloading this plugin returns only after both faces
|
|
|
- * of the mounted backend (and their dependents) quiesced.
|
|
|
+ * Client surface package per resolved kind, mounted with its backend so one
|
|
|
+ * resolved interaction still composes both faces. Declared as dependencies by
|
|
|
+ * every composing app for the same reason as {@link BACKEND_PACKAGES}.
|
|
|
+ */
|
|
|
+export const SURFACE_PACKAGES: Record<DirectoryPickerBackendKind, string> = {
|
|
|
+ native: '@deepseek-ai/dsh-client-ui-directory-picker-native',
|
|
|
+ browse: '@deepseek-ai/dsh-client-ui-directory-picker',
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Resolve the interaction from one boot-time sample and mount its backend and
|
|
|
+ * surface as Loader entries; the effect's disposer removes both entries and
|
|
|
+ * joins their fibers' teardown, so unloading this plugin returns only after
|
|
|
+ * both faces of the mounted interaction (and their dependents) quiesced.
|
|
|
* @param ctx - cordis context carrying the injected `httpServer` and `loader`.
|
|
|
*/
|
|
|
export async function apply(ctx: Context): Promise<void> {
|
|
|
@@ -54,16 +65,22 @@ export async function apply(ctx: Context): Promise<void> {
|
|
|
})
|
|
|
await ctx.effect(async () => {
|
|
|
// Root-tree create: the Loader root is in-memory (write() is a no-op), so
|
|
|
- // the mounted row can never be persisted back into a config file.
|
|
|
- const id = await ctx.loader.create({ name: BACKEND_PACKAGES[backend] })
|
|
|
+ // the mounted rows can never be persisted back into a config file. The
|
|
|
+ // backend lands first: the surface's browser half drives the capability
|
|
|
+ // the backend registers.
|
|
|
+ const ids: string[] = []
|
|
|
+ for (const name of [BACKEND_PACKAGES[backend], SURFACE_PACKAGES[backend]]) {
|
|
|
+ ids.push(await ctx.loader.create({ name }))
|
|
|
+ }
|
|
|
return async () => {
|
|
|
- // Tree teardown (group.stop) can have removed the entry already;
|
|
|
- // nothing is left to unmount or await then.
|
|
|
- const entry = ctx.loader.store[id]
|
|
|
- if (entry === undefined) return
|
|
|
- // remove() disposes the entry transactionally, so the chooser's unload
|
|
|
- // signals completion only after the backend quiesced.
|
|
|
- await ctx.loader.remove(id)
|
|
|
+ for (const id of ids.reverse()) {
|
|
|
+ // Tree teardown (group.stop) can have removed the entry already;
|
|
|
+ // nothing is left to unmount or await then.
|
|
|
+ if (ctx.loader.store[id] === undefined) continue
|
|
|
+ // remove() disposes the entry transactionally, so the chooser's unload
|
|
|
+ // signals completion only after that face quiesced.
|
|
|
+ await ctx.loader.remove(id)
|
|
|
+ }
|
|
|
}
|
|
|
- }, 'directory-picker-auto: backend entry')
|
|
|
+ }, 'directory-picker-auto: interaction entries')
|
|
|
}
|