@wingsky-1/dsh-worktree-sidebar 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +186 -0
  3. package/README.md +190 -0
  4. package/cordis.patch.yml +9 -0
  5. package/lib/client/bindings.d.ts +16 -0
  6. package/lib/client/index.d.ts +23 -0
  7. package/lib/client/inject.d.ts +21 -0
  8. package/lib/client/shared/ports.d.ts +141 -0
  9. package/lib/client/source.d.ts +22 -0
  10. package/lib/client/takeover.d.ts +51 -0
  11. package/lib/client.js +394 -0
  12. package/lib/index.d.ts +28 -0
  13. package/lib/index.js +1614 -0
  14. package/lib/server/api/deps.d.ts +35 -0
  15. package/lib/server/api/impl/handlers/index.d.ts +24 -0
  16. package/lib/server/api/impl/route/index.d.ts +25 -0
  17. package/lib/server/api/impl/service/index.d.ts +31 -0
  18. package/lib/server/api/interface.d.ts +12 -0
  19. package/lib/server/binding/deps.d.ts +9 -0
  20. package/lib/server/binding/impl/model/index.d.ts +16 -0
  21. package/lib/server/binding/impl/model/type.d.ts +37 -0
  22. package/lib/server/binding/impl/service/index.d.ts +67 -0
  23. package/lib/server/binding/impl/store/index.d.ts +6 -0
  24. package/lib/server/binding/interface.d.ts +24 -0
  25. package/lib/server/git/deps.d.ts +59 -0
  26. package/lib/server/git/impl/exec/index.d.ts +3 -0
  27. package/lib/server/git/impl/inspect/index.d.ts +47 -0
  28. package/lib/server/git/impl/service/index.d.ts +70 -0
  29. package/lib/server/git/interface.d.ts +41 -0
  30. package/lib/server/host/agents.d.ts +39 -0
  31. package/lib/server/host/sessions.d.ts +41 -0
  32. package/lib/server/host/typert.d.ts +11 -0
  33. package/lib/server/scope/deps.d.ts +110 -0
  34. package/lib/server/scope/impl/inherit/index.d.ts +25 -0
  35. package/lib/server/scope/impl/own/index.d.ts +21 -0
  36. package/lib/server/scope/impl/resolve/index.d.ts +40 -0
  37. package/lib/server/scope/impl/service/index.d.ts +115 -0
  38. package/lib/server/scope/interface.d.ts +40 -0
  39. package/lib/server/shared/file-io.d.ts +19 -0
  40. package/lib/server/shared/interface.d.ts +11 -0
  41. package/lib/server/shared/paths.d.ts +2 -0
  42. package/lib/server/shared/type.d.ts +6 -0
  43. package/lib/server/tools/deps.d.ts +52 -0
  44. package/lib/server/tools/impl/bind/index.d.ts +62 -0
  45. package/lib/server/tools/impl/create/index.d.ts +4 -0
  46. package/lib/server/tools/impl/protocol/index.d.ts +36 -0
  47. package/lib/server/tools/impl/register/index.d.ts +4 -0
  48. package/lib/server/tools/impl/remove/index.d.ts +12 -0
  49. package/lib/server/tools/impl/service/index.d.ts +28 -0
  50. package/lib/server/tools/impl/session/index.d.ts +18 -0
  51. package/lib/server/tools/interface.d.ts +14 -0
  52. package/lib/shared/contract.d.ts +26 -0
  53. package/lib/shared/interface.d.ts +8 -0
  54. package/package.json +103 -0
  55. package/shared/client/ensure-style.d.ts +21 -0
  56. package/shared/client/i18n.d.ts +15 -0
  57. package/shared/dsh-home.d.ts +15 -0
  58. package/shared/host-utils.d.ts +65 -0
  59. package/shared/loopback.d.ts +21 -0
  60. package/shared/settings-namespace.d.ts +46 -0
  61. package/shared/sse-hub.d.ts +76 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 伪造的 `sessions` hook 源:把某个会话的 `cwd` 改写成 worktree 路径,其余字段原样透传。
3
+ *
4
+ * 三条硬约束,每一条失效的形态都很难查:
5
+ * 1. **引用稳定**:同一次真实快照 + 同一条 worktree 路径必须回同一个对象。每次新建对象会让渲染层
6
+ * 认为状态一直在变,表现是右栏持续重渲染(刷得看不出来,只是卡)。
7
+ * 2. **只改一个字段**:ById 里其余会话、以及该会话的其余字段都必须是原引用,否则预览、命令面板等
8
+ * 消费方会看到被我们改过的世界。
9
+ * 3. **未绑定即原样**:没有绑定就直接回真实快照,不制造副本——那时我们的存在应当完全不可见。
10
+ */
11
+ import type { ObservablePort, SessionsSnapshotLike } from "./shared/ports.js";
12
+ /** 一个会话的改写源。 */
13
+ interface SessionsSource extends ObservablePort<SessionsSnapshotLike> {
14
+ /** 通知订阅者状态可能变了(由绑定刷新驱动)。 */
15
+ notify(): void;
16
+ }
17
+ /**
18
+ * @param real - 真实会话源(官方注入的那一个)。
19
+ * @param currentPath - 同步读当前生效的 worktree 路径;null 表示按真实 cwd 走。
20
+ */
21
+ export declare function createSessionsSource(real: ObservablePort<SessionsSnapshotLike>, sessionId: string, currentPath: () => string | null): SessionsSource;
22
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 接管 files 页签:把官方组件的**目录根**换掉,不重写树 UI。
3
+ *
4
+ * 接管只做一件事:以**官方 id** 为 key、**更低的 priority** 再登记一条正文,组件 / store /
5
+ * locale 全部复用官方那条(inject 面由 `inject.ts` 包一层)。官方 ui-slots 的每个 cell 取
6
+ * 「优先级最低的那条存活项」——它的注册表原话是 register at a different priority to shadow it
7
+ * (lowest renders)(`@deepseek-ai/dsh-client-ui-slots` 的 `lib/index.js:77`;排序 `:130`;取当值项 `:187-200`)。
8
+ *
9
+ * **那个包不在 dsh 安装树里**:它在构建期被内联进 `dsh-web-frontend/dist/assets/index-*.js`,
10
+ * 所以要照注释去读原文时,可读副本在同仓 catalog 锁版处
11
+ * (`node_modules/.pnpm/@deepseek-ai+dsh-client-ui-slots@0.1.5-rc.1_...` 下的
12
+ * `node_modules/@deepseek-ai/dsh-client-ui-slots/lib/index.js`)。
13
+ * 树内可读的两处是类型面与运行时入口:`dsh-client-ui-renderer/lib/types/client/registry.d.ts:46/84/154/164`
14
+ * (`class SlotRegistry`、`register`、`entries`、`entriesOfSlot`)与同一包 `lib/client.js:953/1191-1198/1388`。
15
+ *
16
+ * **为什么不再顶掉类型表**:顶掉就得把官方定义整份搬运(`guide` / `patterns` / `canOpen` 以及
17
+ * 将来新增的字段),官方注册表 refresh 后还会用**在册定义**重算 guide 条目——搬丢 `guide` 的后果
18
+ * 是所有会话的默认页签变成空的 Guide。遮蔽正文让类型表、标题、guide 全部保持原样,这份搬运消失。
19
+ *
20
+ * **当值判定**:登记之后座位上当值的那条必须带我们的 priority,否则说明遮蔽没生效(官方换了更低的
21
+ * 优先级,或我们的条目已被退场)。判定失败当场撤掉自己那条并出声:可感知地什么都不做,好过静默
22
+ * 显示错的地方。
23
+ *
24
+ * 本文件只经 `TakeoverDeps.wrapInject` 拿到改写器——接管块与改写块零互引,装配由 `index.ts` 完成。
25
+ */
26
+ import type { ClientSlotsPort, StoredEntryLike, TabsPort, WrapInject } from "./shared/ports.js";
27
+ /** 我们接管的 kind。 */
28
+ export declare const FILES_KIND = "files";
29
+ /** 正文座位。 */
30
+ export declare const BODY_SLOT = "sidebar.right.pane.tab";
31
+ /**
32
+ * 我们那条正文的 priority:比官方那条**低一档**。
33
+ *
34
+ * 官方当前那条没声明 priority(它的 register options 只有 name/key/locale/store/inject,
35
+ * 见界面包里那处注册),所以实际取值仍是 -1;但「官方是 0」不是契约,写死常量意味着
36
+ * 官方哪天声明了 0 以下的 priority,我们就从遮蔽者变成被遮蔽者(自检会当场退位,接管静默失效)。
37
+ * 取值的唯一约束是「比官方低且与官方不同」——同 key 同 priority 的登记会当场抛。
38
+ */
39
+ export declare function shadowPriorityOf(official: StoredEntryLike): number;
40
+ interface TakeoverDeps {
41
+ readonly slots: ClientSlotsPort;
42
+ readonly tabs: TabsPort;
43
+ readonly logger: {
44
+ warn(message: string): void;
45
+ };
46
+ /** 官方 inject 面的改写器:由装配根从 `inject.ts` 接进来(块间零互引)。 */
47
+ readonly wrapInject: WrapInject;
48
+ }
49
+ /** 接管入口。返回值是完整释放函数;调用方把它放进 `ctx.effect` 的 disposer。 */
50
+ export declare function installTakeover(deps: TakeoverDeps): () => void;
51
+ export {};
package/lib/client.js ADDED
@@ -0,0 +1,394 @@
1
+ "use strict";
2
+ (() => {
3
+ // <define:__DSH_ROUTES__>
4
+ var define_DSH_ROUTES_default = { bindings: "/api/dsh-worktree-sidebar/bindings", health: "/api/dsh-worktree-sidebar/health" };
5
+
6
+ // src/shared/contract.ts
7
+ var ROUTES = {
8
+ bindings: "/api/dsh-worktree-sidebar/bindings",
9
+ health: "/api/dsh-worktree-sidebar/health"
10
+ };
11
+
12
+ // src/client/bindings.ts
13
+ function createBindingState(read, sessionId) {
14
+ let path = null;
15
+ let revision = -1;
16
+ const listeners = /* @__PURE__ */ new Set();
17
+ return {
18
+ getSnapshot: () => path,
19
+ subscribe(listener) {
20
+ listeners.add(listener);
21
+ return () => {
22
+ listeners.delete(listener);
23
+ };
24
+ },
25
+ async refresh() {
26
+ let next;
27
+ try {
28
+ next = await read(sessionId);
29
+ } catch {
30
+ return;
31
+ }
32
+ if (next === void 0) return;
33
+ if (next.revision === revision && next.worktreePath === path) return;
34
+ if (next.revision < revision) return;
35
+ revision = next.revision;
36
+ path = next.worktreePath;
37
+ for (const listener of [...listeners]) listener();
38
+ }
39
+ };
40
+ }
41
+
42
+ // src/client/inject.ts
43
+ var liveSeedings = /* @__PURE__ */ new Set();
44
+ function releaseAllSeedings() {
45
+ for (const release of [...liveSeedings]) release();
46
+ }
47
+ function createInjectWrapper(viewFor) {
48
+ return (official) => {
49
+ return (...args) => {
50
+ const face = official === void 0 ? {} : official(...args);
51
+ const sessionId = args.find((arg) => typeof arg === "string");
52
+ if (sessionId === void 0) return face;
53
+ const view = viewFor(sessionId);
54
+ const bound = { ...face, hooks: mergeSessions(face, view) };
55
+ return typeof face["start"] === "function" ? { ...bound, ...seeding(view, face) } : bound;
56
+ };
57
+ };
58
+ }
59
+ function mergeSessions(face, view) {
60
+ const existing = face["hooks"];
61
+ const hooks = typeof existing === "object" && existing !== null ? existing : {};
62
+ return { ...hooks, sessions: view.source };
63
+ }
64
+ function seeding(view, face) {
65
+ const officialStart = face["start"];
66
+ const officialLoad = face["load"];
67
+ const watched = /* @__PURE__ */ new Map();
68
+ let unsubscribe;
69
+ let detachVisible;
70
+ const refresh = () => {
71
+ void view.root.refresh();
72
+ };
73
+ const reseed = () => {
74
+ const next = view.root.getSnapshot();
75
+ for (const [tabId, tab] of [...watched]) {
76
+ if (tab.signal?.aborted === true) continue;
77
+ const root = next ?? tab.fallback;
78
+ if (root === tab.seeded) continue;
79
+ tab.seeded = root;
80
+ officialStart(tabId, root, tab.signal);
81
+ }
82
+ };
83
+ const release = () => {
84
+ watched.clear();
85
+ liveSeedings.delete(release);
86
+ unsubscribe?.();
87
+ unsubscribe = void 0;
88
+ detachVisible?.();
89
+ detachVisible = void 0;
90
+ };
91
+ const watchAbort = (tabId, signal) => {
92
+ signal?.addEventListener(
93
+ "abort",
94
+ () => {
95
+ watched.delete(tabId);
96
+ if (watched.size === 0) release();
97
+ },
98
+ { once: true }
99
+ );
100
+ };
101
+ const attach = (tabId, fallback, seeded, signal) => {
102
+ liveSeedings.add(release);
103
+ watched.set(tabId, { seeded, fallback, signal });
104
+ unsubscribe ?? (unsubscribe = view.root.subscribe(reseed));
105
+ detachVisible ?? (detachVisible = refreshWhenVisible(refresh));
106
+ watchAbort(tabId, signal);
107
+ };
108
+ const start = (tabId, root, signal) => {
109
+ attach(tabId, root, root, signal);
110
+ officialStart(tabId, root, signal);
111
+ void view.root.refresh().then(() => {
112
+ if (signal?.aborted === true) return;
113
+ const seeded = view.root.getSnapshot() ?? root;
114
+ const tab = watched.get(tabId);
115
+ if (tab === void 0 || tab.seeded === seeded) return;
116
+ tab.seeded = seeded;
117
+ officialStart(tabId, seeded, tab.signal);
118
+ });
119
+ };
120
+ const load = (tabId, path, signal) => {
121
+ if (watched.get(tabId)?.seeded === path) refresh();
122
+ officialLoad?.(tabId, path, signal);
123
+ };
124
+ return officialLoad === void 0 ? { start } : { start, load };
125
+ }
126
+ function refreshWhenVisible(refresh) {
127
+ if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
128
+ const onVisible = () => {
129
+ if (!document.hidden) refresh();
130
+ };
131
+ document.addEventListener("visibilitychange", onVisible);
132
+ window.addEventListener("focus", onVisible);
133
+ return () => {
134
+ document.removeEventListener("visibilitychange", onVisible);
135
+ window.removeEventListener("focus", onVisible);
136
+ };
137
+ }
138
+
139
+ // src/client/source.ts
140
+ function createSessionsSource(real, sessionId, currentPath) {
141
+ const listeners = /* @__PURE__ */ new Set();
142
+ let cached;
143
+ let cachedFrom;
144
+ let cachedPath = null;
145
+ return {
146
+ getSnapshot() {
147
+ const snapshot = real.getSnapshot();
148
+ const path = currentPath();
149
+ if (path === null) return snapshot;
150
+ if (cached !== void 0 && cachedFrom === snapshot && cachedPath === path) return cached;
151
+ cachedFrom = snapshot;
152
+ cachedPath = path;
153
+ cached = rewrite(snapshot, sessionId, path);
154
+ return cached;
155
+ },
156
+ subscribe(listener) {
157
+ const unsubscribeReal = real.subscribe(listener);
158
+ listeners.add(listener);
159
+ return () => {
160
+ listeners.delete(listener);
161
+ unsubscribeReal();
162
+ };
163
+ },
164
+ notify() {
165
+ for (const listener of [...listeners]) listener();
166
+ }
167
+ };
168
+ }
169
+ function rewrite(snapshot, sessionId, path) {
170
+ const byId = snapshot.byId;
171
+ if (byId === void 0) return snapshot;
172
+ const entry = byId[sessionId];
173
+ if (typeof entry !== "object" || entry === null) return snapshot;
174
+ return {
175
+ ...snapshot,
176
+ byId: { ...byId, [sessionId]: { ...entry, cwd: path } }
177
+ };
178
+ }
179
+
180
+ // src/client/takeover.ts
181
+ var FILES_KIND = "files";
182
+ var BODY_SLOT = "sidebar.right.pane.tab";
183
+ function shadowPriorityOf(official) {
184
+ return (official.options.priority ?? 0) - 1;
185
+ }
186
+ var TRANSIENT_ATTEMPTS = 2;
187
+ function installTakeover(deps) {
188
+ let live = null;
189
+ const ours = /* @__PURE__ */ new Set();
190
+ const attempts = /* @__PURE__ */ new WeakMap();
191
+ let refused = null;
192
+ const officialId = () => deps.tabs.get(FILES_KIND)?.id;
193
+ const isOurs = (entry) => entry.options.priority !== void 0 && ours.has(entry.options.priority);
194
+ const findOfficial = (id) => {
195
+ let best;
196
+ for (const entry of deps.slots.entries(BODY_SLOT)) {
197
+ if (entry.options.key !== id || isOurs(entry)) continue;
198
+ if (best === void 0 || (entry.options.priority ?? 0) > (best.options.priority ?? 0)) {
199
+ best = entry;
200
+ }
201
+ }
202
+ return best;
203
+ };
204
+ const isWinner = (id, priority) => deps.slots.entriesOfSlot(BODY_SLOT).some((entry) => entry.options.key === id && entry.options.priority === priority);
205
+ const teardown = () => {
206
+ const current = live;
207
+ live = null;
208
+ if (current === null) return;
209
+ ours.delete(current.priority);
210
+ try {
211
+ current.dispose();
212
+ } catch {
213
+ }
214
+ };
215
+ class ShadowRefused extends Error {
216
+ }
217
+ const tryTakeOver = (official, id) => {
218
+ const priority = shadowPriorityOf(official);
219
+ let dispose;
220
+ try {
221
+ dispose = deps.slots.register(
222
+ {
223
+ name: BODY_SLOT,
224
+ key: id,
225
+ priority,
226
+ locale: official.locale,
227
+ store: official.store,
228
+ inject: deps.wrapInject(official.inject)
229
+ },
230
+ official.component
231
+ );
232
+ if (!isWinner(id, priority)) {
233
+ throw new ShadowRefused("遮蔽未生效:当值项不是我们这条(priority 更低者才渲染)");
234
+ }
235
+ } catch (cause) {
236
+ try {
237
+ dispose?.();
238
+ } catch {
239
+ }
240
+ const reason = cause instanceof Error ? cause.message : String(cause);
241
+ deps.logger.warn(
242
+ "dsh-worktree-sidebar: 接管 files 页签正文失败,已退位(保持官方行为)— " + reason
243
+ );
244
+ if (cause instanceof ShadowRefused) {
245
+ refused = official;
246
+ return;
247
+ }
248
+ const tries = (attempts.get(official) ?? 0) + 1;
249
+ attempts.set(official, tries);
250
+ if (tries >= TRANSIENT_ATTEMPTS) refused = official;
251
+ return;
252
+ }
253
+ attempts.delete(official);
254
+ refused = null;
255
+ ours.add(priority);
256
+ live = { dispose, shadowed: official, priority };
257
+ };
258
+ const evaluate = () => {
259
+ const id = officialId();
260
+ if (id === void 0) {
261
+ teardown();
262
+ return;
263
+ }
264
+ const official = findOfficial(id);
265
+ if (official === void 0) {
266
+ teardown();
267
+ return;
268
+ }
269
+ if (live !== null) {
270
+ if (live.shadowed === official) {
271
+ if (isWinner(id, live.priority)) return;
272
+ teardown();
273
+ deps.logger.warn(
274
+ "dsh-worktree-sidebar: 接管 files 页签正文失败,已退位(当值项被更低的 priority 夺走,我们的包装不会渲染)"
275
+ );
276
+ refused = official;
277
+ return;
278
+ }
279
+ teardown();
280
+ } else if (official === refused) {
281
+ return;
282
+ } else if (!deps.slots.entriesOfSlot(BODY_SLOT).some((e) => e.options.key === id)) {
283
+ return;
284
+ }
285
+ tryTakeOver(official, id);
286
+ };
287
+ const unsubscribeSlot = deps.slots.subscribe(BODY_SLOT, evaluate);
288
+ let unsubscribeErrors;
289
+ try {
290
+ unsubscribeErrors = deps.slots.onEntryError((key, entry, error) => {
291
+ if (key !== BODY_SLOT || !isOurs(entry)) return;
292
+ deps.logger.warn(
293
+ "dsh-worktree-sidebar: 接管入口出现异常(" + key + ")— " + (error instanceof Error ? error.message : String(error))
294
+ );
295
+ });
296
+ evaluate();
297
+ } catch (cause) {
298
+ try {
299
+ unsubscribeErrors?.();
300
+ } catch {
301
+ }
302
+ try {
303
+ unsubscribeSlot();
304
+ } catch {
305
+ }
306
+ throw cause;
307
+ }
308
+ return () => {
309
+ teardown();
310
+ try {
311
+ unsubscribeSlot();
312
+ } catch {
313
+ }
314
+ try {
315
+ unsubscribeErrors?.();
316
+ } catch {
317
+ }
318
+ };
319
+ }
320
+
321
+ // src/client/index.ts
322
+ var ROUTES_INJECTED = typeof define_DSH_ROUTES_default !== "undefined" ? define_DSH_ROUTES_default : ROUTES;
323
+ var BINDINGS_URL = ROUTES_INJECTED.bindings;
324
+ var readBinding = async (sessionId) => {
325
+ try {
326
+ const response = await fetch(BINDINGS_URL + "?session=" + encodeURIComponent(sessionId), {
327
+ headers: { accept: "application/json" }
328
+ });
329
+ if (!response.ok) return void 0;
330
+ const body = await response.json();
331
+ if (typeof body.revision !== "number") return void 0;
332
+ return {
333
+ revision: body.revision,
334
+ worktreePath: typeof body.worktreePath === "string" ? body.worktreePath : null
335
+ };
336
+ } catch {
337
+ return void 0;
338
+ }
339
+ };
340
+ var VIEW_CACHE_MAX = 128;
341
+ function apply(ctx) {
342
+ try {
343
+ const views = /* @__PURE__ */ new Map();
344
+ const viewFor = (sessionId) => {
345
+ const cached = views.get(sessionId);
346
+ if (cached !== void 0) {
347
+ views.delete(sessionId);
348
+ views.set(sessionId, cached);
349
+ return cached;
350
+ }
351
+ const state = createBindingState(readBinding, sessionId);
352
+ const source = createSessionsSource(ctx.sessions.list, sessionId, () => state.getSnapshot());
353
+ state.subscribe(() => source.notify());
354
+ const view = { source, root: state };
355
+ views.set(sessionId, view);
356
+ if (views.size > VIEW_CACHE_MAX) {
357
+ const coldest = views.keys().next().value;
358
+ if (coldest !== void 0) views.delete(coldest);
359
+ }
360
+ return view;
361
+ };
362
+ const restore = installTakeover({
363
+ slots: ctx.slots,
364
+ tabs: ctx.sidebarRightTabs,
365
+ logger: { warn: (message) => console.warn(message) },
366
+ wrapInject: createInjectWrapper(viewFor)
367
+ });
368
+ ctx.effect(
369
+ () => () => {
370
+ restore();
371
+ releaseAllSeedings();
372
+ views.clear();
373
+ },
374
+ "dsh-worktree-sidebar: 会话视图与 files 页签接管"
375
+ );
376
+ } catch (error) {
377
+ console.warn("[dsh-worktree-sidebar] mount failed:", error);
378
+ }
379
+ }
380
+ var inject = ["slots", "sidebarRightTabs", "sessions", "locale"];
381
+
382
+ // src/client/client-wrapper.ts
383
+ window.__ModuleLoader__.load({
384
+ id: "@wingsky-1/dsh-worktree-sidebar",
385
+ factory: function(require2) {
386
+ var module = { exports: {} };
387
+ var exports = module.exports;
388
+ exports.apply = apply;
389
+ exports.inject = inject;
390
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
391
+ return module.exports;
392
+ }
393
+ });
394
+ })();
package/lib/index.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 宿主端组合根:收窄宿主上下文、按依赖顺序装配各域、卸载逆序释放。
3
+ *
4
+ * `ctx` 的知识只在这里出现——两个宿主适配器各自的官方形状收窄都写在下面对应的 `bind*` 调用里,
5
+ * 适配逻辑住 `src/server/host/`(宿主 rc 演进时只动那几个小文件)。五个域因此都拿不到 `ctx`,
6
+ * 也都能脱离 cordis 被单测驱动。
7
+ */
8
+ import type { Context } from "@deepseek-ai/cordis";
9
+ import { ROUTES } from "./shared/interface.js";
10
+ /** 路由清单经这里出去,构建期注入客户端(bundle-host.ts 只认入口的 `ROUTES` 导出)。 */
11
+ export { ROUTES };
12
+ /** 稳定的 cordis 插件名。 */
13
+ export declare const name = "worktree-sidebar";
14
+ /**
15
+ * 依赖的宿主服务。声明成依赖之后由框架保证服务就绪才轮到装配。
16
+ *
17
+ * `typert` 是接管 `workspaceFileScope` 的唯一入口;`sessions` 只用来读**父链**(子 agent 继承父会话的
18
+ * 登记要靠它)。`sandboxPolicy` 曾经也在这里,是「provider 缺失时自己复刻官方默认语义」那套兜底的输入,
19
+ * 兜底删掉后它没有消费方了。
20
+ */
21
+ export declare const inject: string[];
22
+ /** 组合层入口配置。只有总开关(经插件配置传入):不在盘上读用户配置文件,绑定表由插件在 DSH_HOME 下自持。 */
23
+ export interface WorktreeSidebarConfig {
24
+ /** 总开关;`false` 时一律不接管、不注册工具、不挂路由。 */
25
+ enabled?: boolean;
26
+ }
27
+ /** 挂载 dsh-worktree-sidebar。 */
28
+ export declare function apply(ctx: Context, config?: WorktreeSidebarConfig): Promise<void>;