@shendeguize/dsh-agent-sidecar 0.1.0

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/cordis.patch.yml +10 -0
  4. package/lib/client.js +8062 -0
  5. package/lib/client.js.map +1 -0
  6. package/lib/index.d.ts +396 -0
  7. package/lib/index.js +4166 -0
  8. package/package.json +101 -0
  9. package/src/analysis.ts +782 -0
  10. package/src/bridge.ts +841 -0
  11. package/src/client/analysis/AnalysisPanel.tsx +191 -0
  12. package/src/client/analysis/analysis.module.css +183 -0
  13. package/src/client/analysis-glue.ts +331 -0
  14. package/src/client/api.ts +380 -0
  15. package/src/client/board/Board.tsx +214 -0
  16. package/src/client/board/board.module.css +302 -0
  17. package/src/client/board/logic.ts +556 -0
  18. package/src/client/board/project-view-logic.ts +361 -0
  19. package/src/client/board/project-view.module.css +307 -0
  20. package/src/client/board/project-view.tsx +189 -0
  21. package/src/client/board/strings.ts +112 -0
  22. package/src/client/commands.ts +484 -0
  23. package/src/client/controller.ts +360 -0
  24. package/src/client/css-modules.d.ts +11 -0
  25. package/src/client/detail/SessionDetail.tsx +270 -0
  26. package/src/client/detail/detail.module.css +433 -0
  27. package/src/client/detail/logic.ts +779 -0
  28. package/src/client/detail/strings.ts +98 -0
  29. package/src/client/detail/transport.ts +175 -0
  30. package/src/client/detail-glue.ts +397 -0
  31. package/src/client/detail-view.module.css +79 -0
  32. package/src/client/detail-view.tsx +233 -0
  33. package/src/client/dsh-tools/LineageTree.tsx +210 -0
  34. package/src/client/dsh-tools/SearchPanel.tsx +169 -0
  35. package/src/client/dsh-tools/dsh-tools.module.css +374 -0
  36. package/src/client/dsh-tools/logic.ts +596 -0
  37. package/src/client/dsh-tools/strings.ts +90 -0
  38. package/src/client/index.ts +315 -0
  39. package/src/client/inject/InjectPanel.tsx +482 -0
  40. package/src/client/inject/inject.module.css +446 -0
  41. package/src/client/inject/logic.ts +516 -0
  42. package/src/client/inject/overlay.module.css +22 -0
  43. package/src/client/inject-glue.ts +171 -0
  44. package/src/client/locales/command.ts +48 -0
  45. package/src/client/locales/en.ts +385 -0
  46. package/src/client/locales/index.ts +123 -0
  47. package/src/client/locales/zh.ts +402 -0
  48. package/src/client/m3-transport.ts +151 -0
  49. package/src/client/mount.tsx +307 -0
  50. package/src/client/project-glue.ts +134 -0
  51. package/src/client/search-glue.ts +143 -0
  52. package/src/client/settings-card.module.css +359 -0
  53. package/src/client/settings-card.tsx +565 -0
  54. package/src/client/settings-glue.ts +130 -0
  55. package/src/client/sidebar-tab.tsx +494 -0
  56. package/src/client/sse.ts +366 -0
  57. package/src/client/widget.tsx +80 -0
  58. package/src/config.ts +193 -0
  59. package/src/dsh-inject.ts +240 -0
  60. package/src/fusion.ts +988 -0
  61. package/src/guard.ts +274 -0
  62. package/src/index.ts +950 -0
  63. package/src/inject-gateway.ts +574 -0
  64. package/src/routes.ts +1133 -0
  65. package/src/send-cli.ts +340 -0
  66. package/src/session-store.ts +184 -0
  67. package/src/skills-provider.ts +293 -0
  68. package/src/supervisor.ts +463 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Cross-agent search data glue (T5.10b): a framework-free store feeding
3
+ * the controlled SearchPanel. Normalization (matchedBy vocabulary, snippet
4
+ * highlighting) stays in dsh-tools/logic.ts — this store owns the query
5
+ * box state and transport orchestration only.
6
+ *
7
+ * Submit model: explicit user submit (no as-you-type dialing). A blank
8
+ * query with no project filter clears the results locally — the host
9
+ * answers 400 `invalid_request` for it, so the store never dials that.
10
+ * Stale results are replaced per settle; a failed submit keeps the last
11
+ * results visible with an error banner (SearchPanel renders error-first).
12
+ *
13
+ * Same store discipline as controller.ts: subscribe/getState for
14
+ * `useSyncExternalStore`, immutable snapshots, dispose() = late no-ops.
15
+ * Out-of-order settles are ignored via a submit ticket.
16
+ *
17
+ * @module
18
+ */
19
+
20
+ import { isApiError, type RequestOptions } from './api.ts'
21
+ import { fetchSearch } from './m3-transport.ts'
22
+ import {
23
+ normalizeSearchItems,
24
+ type SearchItemVM,
25
+ type SearchMode,
26
+ type SearchResponseVM,
27
+ } from './dsh-tools/logic.ts'
28
+
29
+ export interface SearchGlueState {
30
+ /** Controlled query box value. */
31
+ query: string
32
+ /** Query echoed by the last settled response ('' before the first). */
33
+ submittedQuery: string
34
+ /** Mode of the last settled response; 'full-text' until told otherwise. */
35
+ mode: SearchMode
36
+ items: SearchItemVM[]
37
+ loading: boolean
38
+ /** Machine reason code of the last failure, or null. */
39
+ error: string | null
40
+ /** Active project filter sent with submits and echoed back, if any. */
41
+ project: string | null
42
+ }
43
+
44
+ export interface SearchStoreOptions {
45
+ fetchSearchFn?: (
46
+ opts?: RequestOptions & { q?: string; project?: string | null; limit?: number },
47
+ ) => Promise<SearchResponseVM>
48
+ /** Result cap passed to the endpoint (server default if unset). */
49
+ limit?: number
50
+ /** Fixed project filter (e.g. a search scoped from the project view). */
51
+ project?: string | null
52
+ }
53
+
54
+ const INITIAL_STATE: SearchGlueState = {
55
+ query: '',
56
+ submittedQuery: '',
57
+ mode: 'full-text',
58
+ items: [],
59
+ loading: false,
60
+ error: null,
61
+ project: null,
62
+ }
63
+
64
+ export class SearchStore {
65
+ private state: SearchGlueState
66
+ private readonly listeners = new Set<() => void>()
67
+ private readonly fetchSearchFn: NonNullable<SearchStoreOptions['fetchSearchFn']>
68
+ private readonly limit: number | undefined
69
+ private disposed = false
70
+ private ticket = 0
71
+
72
+ constructor(options: SearchStoreOptions = {}) {
73
+ this.fetchSearchFn = options.fetchSearchFn ?? fetchSearch
74
+ this.limit = options.limit
75
+ this.state = { ...INITIAL_STATE, project: options.project ?? null }
76
+ }
77
+
78
+ subscribe = (fn: () => void): (() => void) => {
79
+ this.listeners.add(fn)
80
+ return () => { this.listeners.delete(fn) }
81
+ }
82
+
83
+ getState = (): SearchGlueState => this.state
84
+
85
+ private setState(patch: Partial<SearchGlueState>): void {
86
+ if (this.disposed) return
87
+ this.state = { ...this.state, ...patch }
88
+ for (const fn of [...this.listeners]) fn()
89
+ }
90
+
91
+ /** Controlled input change (no dialing). */
92
+ setQuery(query: string): void {
93
+ this.setState({ query })
94
+ }
95
+
96
+ /** Submit the current query; blank + no project filter clears locally. */
97
+ async submit(): Promise<void> {
98
+ if (this.disposed) return
99
+ const q = this.state.query.trim()
100
+ const project = this.state.project
101
+ if (q === '' && (project === null || project.trim() === '')) {
102
+ this.ticket += 1 // outrun any in-flight settle
103
+ this.setState({
104
+ items: [], submittedQuery: '', loading: false, error: null,
105
+ })
106
+ return
107
+ }
108
+ const ticket = (this.ticket += 1)
109
+ this.setState({ loading: true, error: null })
110
+ try {
111
+ const response = await this.fetchSearchFn({
112
+ q,
113
+ project,
114
+ ...(this.limit !== undefined ? { limit: this.limit } : {}),
115
+ })
116
+ if (this.disposed || ticket !== this.ticket) return
117
+ this.adoptResponse(response)
118
+ } catch (err) {
119
+ if (this.disposed || ticket !== this.ticket) return
120
+ this.setState({
121
+ loading: false,
122
+ error: isApiError(err) ? err.reason : 'network_error',
123
+ })
124
+ }
125
+ }
126
+
127
+ /** Apply one settled wire response (public seam for tests/materialize). */
128
+ adoptResponse(response: SearchResponseVM): void {
129
+ this.setState({
130
+ items: normalizeSearchItems(response),
131
+ mode: response.mode,
132
+ submittedQuery: response.query,
133
+ project: response.project,
134
+ loading: false,
135
+ error: null,
136
+ })
137
+ }
138
+
139
+ dispose(): void {
140
+ this.disposed = true
141
+ this.listeners.clear()
142
+ }
143
+ }
@@ -0,0 +1,359 @@
1
+ /* Agent Sidecar settings card. Design language follows the shipped
2
+ * ui-settings-plugins PluginCard/fields chrome (disclosure header over a
3
+ * sectioned form body); every color rides a dsh --dsw-alias-* design token,
4
+ * no literal colors. */
5
+
6
+ .card {
7
+ list-style: none;
8
+ border: 1px solid var(--dsw-alias-border-l2);
9
+ border-radius: 12px;
10
+ background: var(--dsw-alias-bg-layer-3);
11
+ transition: border-color .16s, background .16s;
12
+ }
13
+
14
+ .card:hover {
15
+ border-color: var(--dsw-alias-label-dimmed);
16
+ }
17
+
18
+ .cardOpen {
19
+ background: var(--dsw-alias-bg-layer-2);
20
+ border-color: var(--dsw-alias-label-dimmed);
21
+ }
22
+
23
+ /* ── disclosure header ─────────────────────────────────────────────────── */
24
+
25
+ .header {
26
+ width: 100%;
27
+ appearance: none;
28
+ border: 0;
29
+ background: none;
30
+ font: inherit;
31
+ color: inherit;
32
+ text-align: left;
33
+ cursor: pointer;
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 12px;
37
+ padding: 14px 16px;
38
+ border-radius: 12px;
39
+ }
40
+
41
+ .header:focus-visible {
42
+ outline: 2px solid var(--dsw-alias-brand-primary);
43
+ outline-offset: -2px;
44
+ }
45
+
46
+ .headText {
47
+ flex: 1;
48
+ min-width: 0;
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: 4px;
52
+ }
53
+
54
+ .name {
55
+ font-size: 15px;
56
+ font-weight: 600;
57
+ line-height: 1.4;
58
+ color: var(--dsw-alias-label-primary);
59
+ }
60
+
61
+ .description {
62
+ font-size: 13px;
63
+ line-height: 1.5;
64
+ color: var(--dsw-alias-label-tertiary);
65
+ }
66
+
67
+ /* Carried on the header so a collapsed card still says it holds edits. */
68
+ .pending {
69
+ flex: none;
70
+ border-radius: 999px;
71
+ padding: 1px 8px;
72
+ font-size: 11px;
73
+ line-height: 17px;
74
+ font-weight: 500;
75
+ white-space: nowrap;
76
+ background: var(--dsw-alias-bg-module-platform);
77
+ color: var(--dsw-alias-label-secondary);
78
+ }
79
+
80
+ /* CSS-drawn caret (ui-primitives icons are not on this package's dependency
81
+ * surface, so the chevron is a rotated border square). */
82
+ .chevron {
83
+ flex: none;
84
+ width: 8px;
85
+ height: 8px;
86
+ border-right: 1.5px solid var(--dsw-alias-label-tertiary);
87
+ border-bottom: 1.5px solid var(--dsw-alias-label-tertiary);
88
+ transform: rotate(45deg) translateY(-2px);
89
+ transition: transform .16s;
90
+ }
91
+
92
+ .chevronOpen {
93
+ transform: rotate(225deg) translateY(-2px);
94
+ }
95
+
96
+ /* ── body ──────────────────────────────────────────────────────────────── */
97
+
98
+ .body {
99
+ border-top: 1px solid var(--dsw-alias-border-l2);
100
+ margin: 0 16px;
101
+ padding-bottom: 8px;
102
+ }
103
+
104
+ .readOnly {
105
+ margin: 12px 0 0;
106
+ font-size: 12px;
107
+ line-height: 1.5;
108
+ color: var(--dsw-alias-label-tertiary);
109
+ }
110
+
111
+ .section {
112
+ padding: 12px 0 4px;
113
+ }
114
+
115
+ .section + .section {
116
+ border-top: 1px solid var(--dsw-alias-border-l2);
117
+ }
118
+
119
+ .sectionTitle {
120
+ margin: 0 0 4px;
121
+ font-size: 13px;
122
+ font-weight: 600;
123
+ line-height: 1.5;
124
+ color: var(--dsw-alias-label-primary);
125
+ }
126
+
127
+ /* ── fields ────────────────────────────────────────────────────────────── */
128
+
129
+ .field {
130
+ padding: 8px 0;
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 6px;
134
+ }
135
+
136
+ .label {
137
+ font-size: 13px;
138
+ line-height: 1.5;
139
+ color: var(--dsw-alias-label-primary);
140
+ }
141
+
142
+ .hint {
143
+ margin: 0;
144
+ font-size: 12px;
145
+ line-height: 1.5;
146
+ color: var(--dsw-alias-label-tertiary);
147
+ }
148
+
149
+ .invalidHint {
150
+ margin: 0;
151
+ font-size: 12px;
152
+ line-height: 1.5;
153
+ color: var(--dsw-alias-label-error);
154
+ }
155
+
156
+ .input,
157
+ .select {
158
+ appearance: none;
159
+ max-width: 420px;
160
+ border: 1px solid var(--dsw-alias-border-l2);
161
+ border-radius: 8px;
162
+ background: var(--dsw-alias-bg-layer-3);
163
+ padding: 6px 10px;
164
+ font: inherit;
165
+ font-size: 13px;
166
+ line-height: 1.5;
167
+ color: var(--dsw-alias-label-primary);
168
+ }
169
+
170
+ .input:focus-visible,
171
+ .select:focus-visible {
172
+ outline: none;
173
+ border-color: var(--dsw-alias-brand-primary);
174
+ }
175
+
176
+ .input::placeholder {
177
+ color: var(--dsw-alias-label-tertiary);
178
+ }
179
+
180
+ .input:disabled,
181
+ .select:disabled {
182
+ opacity: 0.5;
183
+ cursor: default;
184
+ }
185
+
186
+ /* Modifier applied ALONGSIDE .input (no `composes`: the tsdown class-map
187
+ * emitter keeps only the local hashed name and would drop composed bases). */
188
+ .inputInvalid {
189
+ border-color: var(--dsw-alias-label-error);
190
+ }
191
+
192
+ /* Toggle row: label text left, native checkbox right-aligned with it. */
193
+ .toggleRow {
194
+ display: flex;
195
+ align-items: center;
196
+ gap: 10px;
197
+ cursor: pointer;
198
+ }
199
+
200
+ .toggleRow input {
201
+ accent-color: var(--dsw-alias-brand-primary);
202
+ width: 16px;
203
+ height: 16px;
204
+ margin: 0;
205
+ cursor: pointer;
206
+ }
207
+
208
+ .toggleRow input:disabled {
209
+ cursor: default;
210
+ }
211
+
212
+ /* ── notes (safety / daemon guidance) ──────────────────────────────────── */
213
+
214
+ .note {
215
+ margin: 6px 0 2px;
216
+ border-radius: 8px;
217
+ padding: 8px 10px;
218
+ font-size: 12px;
219
+ line-height: 1.6;
220
+ background: var(--dsw-alias-bg-module-platform);
221
+ color: var(--dsw-alias-label-secondary);
222
+ }
223
+
224
+ /* ── daemon status row ─────────────────────────────────────────────────── */
225
+
226
+ .statusRow {
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 8px;
230
+ padding: 4px 0;
231
+ }
232
+
233
+ .statusDot {
234
+ flex: none;
235
+ width: 8px;
236
+ height: 8px;
237
+ border-radius: 50%;
238
+ background: var(--dsw-alias-label-tertiary);
239
+ }
240
+
241
+ /* Modifiers applied alongside .statusDot (same no-`composes` rationale). */
242
+ .statusOk {
243
+ background: var(--dsw-alias-brand-primary);
244
+ }
245
+
246
+ .statusError {
247
+ background: var(--dsw-alias-label-error);
248
+ }
249
+
250
+ .statusText {
251
+ font-size: 13px;
252
+ line-height: 1.5;
253
+ color: var(--dsw-alias-label-primary);
254
+ }
255
+
256
+ .statusMeta {
257
+ font-size: 12px;
258
+ line-height: 1.5;
259
+ color: var(--dsw-alias-label-tertiary);
260
+ }
261
+
262
+ .retry {
263
+ appearance: none;
264
+ border: 1px solid var(--dsw-alias-border-l2);
265
+ border-radius: 8px;
266
+ background: none;
267
+ padding: 3px 12px;
268
+ font: inherit;
269
+ font-size: 12px;
270
+ line-height: 1.5;
271
+ color: var(--dsw-alias-label-secondary);
272
+ cursor: pointer;
273
+ }
274
+
275
+ .retry:hover:not(:disabled) {
276
+ color: var(--dsw-alias-label-primary);
277
+ border-color: var(--dsw-alias-label-dimmed);
278
+ }
279
+
280
+ .retry:focus-visible {
281
+ outline: 2px solid var(--dsw-alias-brand-primary);
282
+ outline-offset: 1px;
283
+ }
284
+
285
+ /* ── footer ────────────────────────────────────────────────────────────── */
286
+
287
+ .footer {
288
+ display: flex;
289
+ align-items: center;
290
+ gap: 8px;
291
+ padding: 12px 0 4px;
292
+ border-top: 1px solid var(--dsw-alias-border-l2);
293
+ }
294
+
295
+ .docs {
296
+ font-size: 12px;
297
+ line-height: 1.5;
298
+ color: var(--dsw-alias-label-tertiary);
299
+ text-decoration: none;
300
+ }
301
+
302
+ .docs:hover {
303
+ color: var(--dsw-alias-label-primary);
304
+ text-decoration: underline;
305
+ }
306
+
307
+ .failed {
308
+ flex: 1;
309
+ min-width: 0;
310
+ margin: 0;
311
+ text-align: right;
312
+ font-size: 12px;
313
+ line-height: 1.5;
314
+ color: var(--dsw-alias-label-error);
315
+ }
316
+
317
+ .spacer {
318
+ flex: 1;
319
+ }
320
+
321
+ .discard,
322
+ .save {
323
+ appearance: none;
324
+ border: 1px solid transparent;
325
+ border-radius: 8px;
326
+ padding: 5px 14px;
327
+ font: inherit;
328
+ font-size: 13px;
329
+ line-height: 1.5;
330
+ cursor: pointer;
331
+ }
332
+
333
+ .discard {
334
+ border-color: var(--dsw-alias-border-l2);
335
+ background: none;
336
+ color: var(--dsw-alias-label-secondary);
337
+ }
338
+
339
+ .discard:hover:not(:disabled) {
340
+ color: var(--dsw-alias-label-primary);
341
+ border-color: var(--dsw-alias-label-dimmed);
342
+ }
343
+
344
+ .save {
345
+ background: var(--dsw-alias-label-primary);
346
+ color: var(--dsw-alias-bg-layer-3);
347
+ }
348
+
349
+ .discard:disabled,
350
+ .save:disabled {
351
+ opacity: 0.4;
352
+ cursor: default;
353
+ }
354
+
355
+ .discard:focus-visible,
356
+ .save:focus-visible {
357
+ outline: 2px solid var(--dsw-alias-brand-primary);
358
+ outline-offset: 1px;
359
+ }