@deepseek-harness-tui/dsh-tui 0.8.4 → 0.8.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 (174) hide show
  1. package/bin/dsh-tui.js +0 -0
  2. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -13
  3. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -59
  4. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -15
  5. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -32
  6. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -59
  7. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -166
  8. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -42
  9. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -143
  10. package/dsh-ecosystem-spec/registry/README.md +14 -14
  11. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -56
  12. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -13
  13. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -149
  14. package/lib/types/activityPrefs.d.ts +10 -0
  15. package/lib/types/activityPrefs.d.ts.map +1 -1
  16. package/lib/types/activityPrefs.js +17 -0
  17. package/lib/types/components/MessageList.d.ts +4 -1
  18. package/lib/types/components/MessageList.d.ts.map +1 -1
  19. package/lib/types/components/MessageList.js +4 -4
  20. package/lib/types/components/SplitDiffView.d.ts +3 -1
  21. package/lib/types/components/SplitDiffView.d.ts.map +1 -1
  22. package/lib/types/components/SplitDiffView.js +12 -5
  23. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  24. package/lib/types/components/StreamingMarkdown.js +10 -2
  25. package/lib/types/components/activityFrames.d.ts +7 -28
  26. package/lib/types/components/activityFrames.d.ts.map +1 -1
  27. package/lib/types/components/activityFrames.js +7 -94
  28. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  29. package/lib/types/components/messages/AssistantThinkingMessage.js +1 -1
  30. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +4 -1
  31. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  32. package/lib/types/components/messages/AssistantToolUseMessage.js +14 -3
  33. package/lib/types/dsh-adapter/channel.d.ts +17 -0
  34. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  35. package/lib/types/dsh-adapter/channel.js +161 -19
  36. package/lib/types/dsh-adapter/index.d.ts +5 -0
  37. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  38. package/lib/types/dsh-adapter/index.js +19 -0
  39. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  40. package/lib/types/dsh-adapter/plugin.js +176 -4
  41. package/lib/types/dsh-adapter/settings-sections.d.ts +13 -0
  42. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  43. package/lib/types/dsh-adapter/settings-sections.js +31 -6
  44. package/lib/types/ink/ink.d.ts +9 -0
  45. package/lib/types/ink/ink.d.ts.map +1 -1
  46. package/lib/types/ink/ink.js +24 -1
  47. package/lib/types/screens/Chat.d.ts.map +1 -1
  48. package/lib/types/screens/Chat.js +34 -4
  49. package/lib/types/screens/Settings.d.ts.map +1 -1
  50. package/lib/types/screens/Settings.js +109 -50
  51. package/lib/types/screens/StatusLine.d.ts +8 -0
  52. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  53. package/lib/types/screens/StatusLine.js +62 -39
  54. package/lib/types/settings-sections.d.ts +1 -1
  55. package/lib/types/settings-sections.d.ts.map +1 -1
  56. package/lib/types/tips.js +2 -2
  57. package/lib/types/tuiDisplayPrefs.d.ts +47 -0
  58. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -0
  59. package/lib/types/tuiDisplayPrefs.js +60 -0
  60. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -8
  61. package/node_modules/@dsh-std/command/LICENSE +21 -21
  62. package/node_modules/@dsh-std/command/README.md +26 -26
  63. package/node_modules/@dsh-std/command/README.zh.md +28 -28
  64. package/node_modules/@dsh-std/command/lib/index.js.map +1 -1
  65. package/node_modules/@dsh-std/command/package.json +22 -22
  66. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  67. package/node_modules/@dsh-std/connection/LICENSE +21 -21
  68. package/node_modules/@dsh-std/connection/README.md +42 -42
  69. package/node_modules/@dsh-std/connection/README.zh.md +42 -42
  70. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  71. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  72. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  73. package/node_modules/@dsh-std/connection/package.json +20 -20
  74. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  75. package/node_modules/@dsh-std/core/LICENSE +21 -21
  76. package/node_modules/@dsh-std/core/README.md +13 -13
  77. package/node_modules/@dsh-std/core/README.zh.md +13 -13
  78. package/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  79. package/node_modules/@dsh-std/core/package.json +21 -21
  80. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  81. package/node_modules/@dsh-std/manifest/LICENSE +21 -21
  82. package/node_modules/@dsh-std/manifest/README.md +5 -5
  83. package/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  84. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  85. package/node_modules/@dsh-std/manifest/package.json +20 -20
  86. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  87. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -8
  88. package/node_modules/@dsh-std/messages/LICENSE +21 -21
  89. package/node_modules/@dsh-std/messages/README.md +5 -5
  90. package/node_modules/@dsh-std/messages/README.zh.md +7 -7
  91. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -1
  92. package/node_modules/@dsh-std/messages/package.json +20 -20
  93. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -54
  94. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  95. package/node_modules/@dsh-std/presentation/LICENSE +21 -21
  96. package/node_modules/@dsh-std/presentation/README.md +27 -27
  97. package/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  98. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  99. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  100. package/node_modules/@dsh-std/presentation/package.json +25 -25
  101. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -8
  102. package/node_modules/@dsh-std/storage/LICENSE +21 -21
  103. package/node_modules/@dsh-std/storage/README.md +5 -5
  104. package/node_modules/@dsh-std/storage/README.zh.md +7 -7
  105. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -1
  106. package/node_modules/@dsh-std/storage/package.json +20 -20
  107. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -56
  108. package/package.json +6 -3
  109. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  110. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -21
  111. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -42
  112. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -42
  113. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  114. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  115. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  116. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -20
  117. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  118. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -21
  119. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -13
  120. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -13
  121. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  122. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -21
  123. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  124. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -21
  125. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -5
  126. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  127. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  128. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -20
  129. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  130. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  131. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -21
  132. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -27
  133. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  134. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  135. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  136. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -25
  137. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  138. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -21
  139. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -13
  140. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -13
  141. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  142. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -21
  143. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  144. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -21
  145. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -13
  146. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -13
  147. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  148. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -21
  149. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  150. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -21
  151. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -13
  152. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -13
  153. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  154. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -21
  155. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  156. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -21
  157. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -42
  158. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -42
  159. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  160. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  161. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  162. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -20
  163. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  164. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -21
  165. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -13
  166. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -13
  167. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  168. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -21
  169. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  170. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -21
  171. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -13
  172. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -13
  173. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  174. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -21
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useTerminalSize, useTheme } from '../ui.js';
3
3
  import { formatTokens } from '../cc/format.js';
4
4
  import { t } from '../i18n.js';
5
+ import { formatContextUsage, normalizeStatusBar } from '../tuiDisplayPrefs.js';
5
6
  import { Byline } from '../components/design-system/Byline.js';
6
7
  import { ActivityLine, contextPressurePct } from '../components/ActivityLine.js';
7
8
  import { modeDisplayName } from '../sessionModes.js';
@@ -18,30 +19,32 @@ import { renderContextBar, renderTpsGauge, renderTpsSparkline, speedColor, } fro
18
19
  export function StatusLine({ channel, selectionActive = false, helpOpen = false, wake, }) {
19
20
  const { columns } = useTerminalSize();
20
21
  const [themeName] = useTheme();
22
+ const statusBar = normalizeStatusBar(channel.statusBar);
21
23
  const usage = channel.lastUsage;
24
+ const contextUsed = usage === undefined
25
+ ? undefined
26
+ : usage.input + usage.cacheRead + usage.cacheWrite;
22
27
  const contextParts = [];
23
- // Session-mode marker (Shift+Tab cycle): hidden on the unmarked base
24
- // mode (index 0); sage while a plan-declaring mode is in force.
25
- if (channel.modeIndex > 0) {
26
- contextParts.push(_jsx(Text, { color: channel.mode.plan === true ? 'planMode' : 'warning', children: modeDisplayName(channel.mode) }, "mode"));
27
- }
28
- if (channel.reasoningEffort !== undefined) {
28
+ if (statusBar.thinking && channel.reasoningEffort !== undefined) {
29
29
  contextParts.push(_jsx(Text, { color: "inactiveShimmer", children: channel.reasoningEffort }, "effort"));
30
30
  }
31
- if (usage !== undefined && usage.cacheRead > 0) {
32
- // Cache hit rate of the context fed to the model (read / total), one
33
- // decimal — the absolute read count lives in the context bar's system
34
- // segment, the rate is the glanceable health signal.
35
- const total = usage.input + usage.cacheRead + usage.cacheWrite;
36
- const rate = total > 0 ? (usage.cacheRead / total) * 100 : 0;
37
- contextParts.push(_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: t('status-cache-label') }), _jsxs(Text, { color: "inactiveShimmer", children: [rate.toFixed(1), "%"] })] }, "cache"));
31
+ if (statusBar.mode && channel.modeIndex > 0) {
32
+ contextParts.push(_jsx(Text, { color: channel.mode.plan === true ? 'planMode' : 'warning', children: modeDisplayName(channel.mode) }, "mode"));
33
+ }
34
+ const formattedContext = statusBar.contextUsage
35
+ ? formatContextUsage(contextUsed, channel.contextWindow, statusBar.compact)
36
+ : undefined;
37
+ const contextUsagePart = formattedContext === undefined
38
+ ? undefined
39
+ : (_jsxs(Text, { color: "inactiveShimmer", children: [_jsx(Text, { dimColor: true, children: "ctx " }), formattedContext] }, "context"));
40
+ if (statusBar.cache) {
41
+ const cacheRate = formatCacheHitRate(usage);
42
+ if (cacheRate !== undefined) {
43
+ contextParts.push(_jsxs(Text, { color: "inactiveShimmer", children: [_jsx(Text, { dimColor: true, children: t('status-cache-label') }), cacheRate] }, "cache"));
44
+ }
38
45
  }
39
- // TPS readout sits right after the model so a crowded footer truncates
40
- // the trailing fields (tokens/think/cache), never the speedometer. One
41
- // number only: the live value (gauge while streaming, sparkline of past
42
- // turns once samples exist) — no μ/p95 clutter.
43
46
  const tpsParts = [];
44
- if (channel.tps !== undefined) {
47
+ if (statusBar.tps && channel.tps !== undefined) {
45
48
  if (channel.working && channel.tpsSamples.length === 0) {
46
49
  tpsParts.push(_jsxs(Text, { children: [renderTpsGauge(channel.tps, channel.tps), ' ', _jsxs(Text, { dimColor: true, children: [Math.round(channel.tps), " tps"] })] }, "tps"));
47
50
  }
@@ -55,57 +58,77 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
55
58
  tpsParts.push(_jsxs(Text, { dimColor: true, children: [Math.round(channel.tps), " t/s"] }, "tps"));
56
59
  }
57
60
  }
58
- // Left group: every field sits at soft white (inactiveShimmer) instead of
59
- // the previous uniform dim grey — readable against dark terminals.
60
61
  const leftParts = [
61
- _jsx(Text, { color: "inactiveShimmer", children: channel.model }, "model"),
62
+ ...(statusBar.model
63
+ ? [_jsx(Text, { color: "inactiveShimmer", children: channel.model }, "model")]
64
+ : []),
62
65
  ...tpsParts,
63
66
  ...contextParts,
64
- _jsxs(Text, { color: "inactiveShimmer", children: [formatTokens(channel.tokens.input), "\u2192", formatTokens(channel.tokens.output)] }, "tokens"),
67
+ ...(statusBar.tokens
68
+ ? [
69
+ _jsxs(Text, { color: "inactiveShimmer", children: [formatTokens(channel.tokens.input), "\u2192", formatTokens(channel.tokens.output)] }, "tokens"),
70
+ ]
71
+ : []),
65
72
  ];
66
- // Right group: git branch in muted steel blue, cwd a soft white, the
67
- // session title dimmest (it truncates first anyway).
68
73
  const rightParts = [
69
- ...(channel.gitBranch
74
+ ...(statusBar.gitBranch && channel.gitBranch
70
75
  ? [
71
76
  _jsx(Text, { color: "professionalBlue", children: channel.gitBranch }, "git"),
72
77
  ]
73
78
  : []),
74
- _jsx(Text, { color: "inactiveShimmer", children: basename(channel.displayCwd) }, "cwd"),
75
- ...(channel.sessionTitle
79
+ ...(statusBar.cwd
80
+ ? [
81
+ _jsx(Text, { color: "inactiveShimmer", children: statusBar.compact ? basename(channel.displayCwd) : channel.displayCwd }, "cwd"),
82
+ ]
83
+ : []),
84
+ ...(statusBar.sessionTitle && channel.sessionTitle
76
85
  ? [
77
86
  _jsx(Text, { dimColor: true, children: channel.sessionTitle }, "title"),
78
87
  ]
79
88
  : []),
80
89
  ];
81
- // Row 3: the mode hint — and, while idle, the working-activity turn
82
- // summary (the live working line itself moves to the spinner slot above
83
- // the input while a turn runs, so the two never duplicate).
84
90
  const hint = selectionActive
85
91
  ? t('statusline-hint-select')
86
92
  : channel.working
87
93
  ? t('statusline-hint-working')
88
- : !helpOpen
94
+ : statusBar.shortcutHint && !helpOpen
89
95
  ? t('statusline-hint-shortcuts')
90
96
  : '';
91
97
  const activity = channel.workingActivity;
92
- const showActivity = !channel.working &&
98
+ const showActivity = statusBar.activity &&
99
+ !channel.working &&
93
100
  activity !== undefined &&
94
101
  activity.line !== '' &&
95
102
  activity.phase !== 'idle';
103
+ const showTrajectory = statusBar.trajectory && wake !== undefined;
96
104
  const barWidth = columns - 4;
97
105
  let bar = null;
98
- // Theme-aware free segment: the light palette's near-white fill (#E8E8E8)
99
- // reads as a glaring white band on dark terminals — swap it for a deep
100
- // blue-gray there while keeping the light palette as-is (dark-ansi carries
101
- // `ansi:` color names, so map by theme name rather than palette tokens).
102
106
  const barColors = themeName === 'light'
103
107
  ? undefined
104
108
  : { freeFill: '#2E3440', freeText: '#8D95A6' };
105
- if (channel.contextBarEnabled && barWidth >= 14 && channel.contextWindow !== undefined) {
106
- bar = renderContextBar(channel.contextSegments, usage !== undefined ? usage.input + usage.cacheRead + usage.cacheWrite : 0, channel.contextWindow, barWidth, barColors);
109
+ if (statusBar.contextBar &&
110
+ channel.contextBarEnabled &&
111
+ barWidth >= 14 &&
112
+ usage !== undefined &&
113
+ channel.contextWindow !== undefined) {
114
+ bar = renderContextBar(channel.contextSegments, contextUsed ?? 0, channel.contextWindow, barWidth, barColors);
107
115
  }
108
- return (_jsx(Box, { paddingX: 1, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [bar ? _jsx(Text, { children: bar }) : null, _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: leftParts }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: rightParts }) }) })] }), _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : hint ? (_jsx(Text, { color: "inactiveShimmer", children: hint })) : null, showActivity && hint ? (_jsx(Text, { color: "inactiveShimmer", wrap: "truncate", children: hint })) : null, wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] })] }) }));
116
+ const compactLeftParts = [...leftParts, ...rightParts];
117
+ const fullLeftParts = contextUsagePart === undefined
118
+ ? leftParts
119
+ : [...leftParts, contextUsagePart];
120
+ const hasStatusFields = compactLeftParts.length > 0 || contextUsagePart !== undefined;
121
+ const showSupplementalRow = hint !== '' || showActivity || showTrajectory;
122
+ return (_jsx(Box, { paddingX: 1, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [bar ? _jsx(Text, { children: bar }) : null, hasStatusFields ? statusBar.compact ? (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: compactLeftParts }) }) }), contextUsagePart ? (_jsx(Box, { flexShrink: 0, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: [contextUsagePart] }) }) })) : null] })) : (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: fullLeftParts }) }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: rightParts }) }) })] })) : null, showSupplementalRow ? _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, justifyContent: showActivity && hint ? 'space-between' : 'flex-start', gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : hint ? (_jsx(Text, { color: "inactiveShimmer", children: hint })) : null, showActivity && hint ? (_jsx(Text, { color: "inactiveShimmer", wrap: "truncate", children: hint })) : null] }), showTrajectory && wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] }) : null] }) }));
123
+ }
124
+ /** Return the prompt-cache hit rate, or nothing when usage is unavailable. */
125
+ export function formatCacheHitRate(usage) {
126
+ if (usage === undefined)
127
+ return undefined;
128
+ const total = usage.input + usage.cacheRead + usage.cacheWrite;
129
+ if (!Number.isFinite(total) || total <= 0)
130
+ return undefined;
131
+ return `${((usage.cacheRead / total) * 100).toFixed(1)}%`;
109
132
  }
110
133
  function basename(path) {
111
134
  const parts = path.split(/[\\/]/);
@@ -1,4 +1,4 @@
1
1
  export { name, TuiSettingsSectionsRuntime } from './dsh-adapter/settings-sections.js';
2
- export type { TuiSettingsFieldKind, TuiSettingsFieldOption, TuiSettingsFieldWrite, TuiSettingsField, TuiSettingsSection, } from './dsh-adapter/settings-sections.js';
2
+ export type { TuiSettingsFieldKind, TuiSettingsFieldOption, TuiSettingsGroup, TuiSettingsFieldWrite, TuiSettingsField, TuiSettingsSection, } from './dsh-adapter/settings-sections.js';
3
3
  export { default } from './dsh-adapter/settings-sections.js';
4
4
  //# sourceMappingURL=settings-sections.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-sections.d.ts","sourceRoot":"","sources":["../../src/settings-sections.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AACrF,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAA"}
1
+ {"version":3,"file":"settings-sections.d.ts","sourceRoot":"","sources":["../../src/settings-sections.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAA;AACrF,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAA"}
package/lib/types/tips.js CHANGED
@@ -443,8 +443,8 @@ export const TIPS = [
443
443
  {
444
444
  id: 'disp-activity',
445
445
  group: 'display',
446
- zh: '/activity frames comet 换状态行动画(31 种)',
447
- en: '/activity frames comet changes the spinner (31 presets)',
446
+ zh: '/activity frames comet 换状态行动画(35 种)',
447
+ en: '/activity frames comet changes the spinner (35 presets)',
448
448
  },
449
449
  {
450
450
  id: 'disp-diff-layout',
@@ -0,0 +1,47 @@
1
+ /** Background treatment applied to tool-call cards. */
2
+ export type ToolBackground = 'none' | 'subtle' | 'strong';
3
+ /** Individually selectable fields in the status footer. */
4
+ export interface StatusBarConfig {
5
+ /** Prefer the compact, single-line presentation when space permits. */
6
+ compact: boolean;
7
+ /** Live model id. */
8
+ model: boolean;
9
+ /** Reasoning effort / thinking mode. */
10
+ thinking: boolean;
11
+ /** Session working directory. */
12
+ cwd: boolean;
13
+ /** Current context-window consumption. */
14
+ contextUsage: boolean;
15
+ /** Prompt-cache hit rate. */
16
+ cache: boolean;
17
+ /** Running input/output token totals. */
18
+ tokens: boolean;
19
+ /** Live and recent output speed. */
20
+ tps: boolean;
21
+ /** Current git branch. */
22
+ gitBranch: boolean;
23
+ /** Current session title. */
24
+ sessionTitle: boolean;
25
+ /** Non-default session mode. */
26
+ mode: boolean;
27
+ /** Segmented context progress bar on its own footer row. */
28
+ contextBar: boolean;
29
+ /** Idle working-activity summary. */
30
+ activity: boolean;
31
+ /** Mini trajectory wake rendered at the footer's right edge. */
32
+ trajectory: boolean;
33
+ /** Idle `? for shortcuts` reminder; shortcut keys remain available when hidden. */
34
+ shortcutHint: boolean;
35
+ }
36
+ /** Defaults keep the essential route/context information visible. */
37
+ export declare const DEFAULT_STATUS_BAR: Readonly<StatusBarConfig>;
38
+ /** Normalize untrusted/config-layer values without mutating the input. */
39
+ export declare function normalizeToolBackground(value: unknown): ToolBackground;
40
+ /** Merge a partial settings value over the stable status-bar defaults. */
41
+ export declare function normalizeStatusBar(value: unknown): StatusBarConfig;
42
+ /**
43
+ * Format context-window usage for future status-line consumers.
44
+ * Invalid or unavailable inputs intentionally produce no field.
45
+ */
46
+ export declare function formatContextUsage(used: number | undefined, contextWindow: number | undefined, compact?: boolean): string | undefined;
47
+ //# sourceMappingURL=tuiDisplayPrefs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tuiDisplayPrefs.d.ts","sourceRoot":"","sources":["../../src/tuiDisplayPrefs.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEzD,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB;IACrB,KAAK,EAAE,OAAO,CAAA;IACd,wCAAwC;IACxC,QAAQ,EAAE,OAAO,CAAA;IACjB,iCAAiC;IACjC,GAAG,EAAE,OAAO,CAAA;IACZ,0CAA0C;IAC1C,YAAY,EAAE,OAAO,CAAA;IACrB,6BAA6B;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAA;IACf,oCAAoC;IACpC,GAAG,EAAE,OAAO,CAAA;IACZ,0BAA0B;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,6BAA6B;IAC7B,YAAY,EAAE,OAAO,CAAA;IACrB,gCAAgC;IAChC,IAAI,EAAE,OAAO,CAAA;IACb,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAA;IACnB,qCAAqC;IACrC,QAAQ,EAAE,OAAO,CAAA;IACjB,gEAAgE;IAChE,UAAU,EAAE,OAAO,CAAA;IACnB,mFAAmF;IACnF,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,qEAAqE;AACrE,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,eAAe,CAgBvD,CAAA;AAKF,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAItE;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CASlE;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,OAAO,UAAO,GACb,MAAM,GAAG,SAAS,CASpB"}
@@ -0,0 +1,60 @@
1
+ /** Defaults keep the essential route/context information visible. */
2
+ export const DEFAULT_STATUS_BAR = Object.freeze({
3
+ compact: true,
4
+ model: true,
5
+ thinking: true,
6
+ cwd: true,
7
+ contextUsage: true,
8
+ cache: true,
9
+ tokens: false,
10
+ tps: false,
11
+ gitBranch: false,
12
+ sessionTitle: false,
13
+ mode: false,
14
+ contextBar: false,
15
+ activity: false,
16
+ trajectory: false,
17
+ shortcutHint: false,
18
+ });
19
+ const TOOL_BACKGROUNDS = new Set(['none', 'subtle', 'strong']);
20
+ const STATUS_BAR_KEYS = Object.keys(DEFAULT_STATUS_BAR);
21
+ /** Normalize untrusted/config-layer values without mutating the input. */
22
+ export function normalizeToolBackground(value) {
23
+ return typeof value === 'string' && TOOL_BACKGROUNDS.has(value)
24
+ ? value
25
+ : 'none';
26
+ }
27
+ /** Merge a partial settings value over the stable status-bar defaults. */
28
+ export function normalizeStatusBar(value) {
29
+ const normalized = { ...DEFAULT_STATUS_BAR };
30
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
31
+ return normalized;
32
+ const input = value;
33
+ for (const key of STATUS_BAR_KEYS) {
34
+ if (typeof input[key] === 'boolean')
35
+ normalized[key] = input[key];
36
+ }
37
+ return normalized;
38
+ }
39
+ function formatTokenCount(value) {
40
+ const rounded = Math.max(0, Math.round(value));
41
+ if (rounded < 1_000)
42
+ return String(rounded);
43
+ if (rounded < 1_000_000)
44
+ return `${(rounded / 1_000).toFixed(rounded < 10_000 ? 1 : 0)}k`;
45
+ return `${(rounded / 1_000_000).toFixed(rounded < 10_000_000 ? 1 : 0)}m`;
46
+ }
47
+ /**
48
+ * Format context-window usage for future status-line consumers.
49
+ * Invalid or unavailable inputs intentionally produce no field.
50
+ */
51
+ export function formatContextUsage(used, contextWindow, compact = true) {
52
+ if (!Number.isFinite(used) || !Number.isFinite(contextWindow) || used === undefined || contextWindow === undefined || contextWindow <= 0) {
53
+ return undefined;
54
+ }
55
+ const safeUsed = Math.max(0, used);
56
+ const percent = Math.min(999, (safeUsed / contextWindow) * 100);
57
+ const percentText = `${percent < 10 ? percent.toFixed(1) : Math.round(percent)}%`;
58
+ const counts = `${formatTokenCount(safeUsed)}/${formatTokenCount(contextWindow)}`;
59
+ return compact ? `${percentText} (${counts})` : `${counts} (${percentText})`;
60
+ }
@@ -1,8 +1,8 @@
1
- # Changelog
2
-
3
- Changes to `@dsh-std/command` are recorded here.
4
-
5
- ## 0.1.0 (draft)
6
-
7
- - Defined portable command trees, arguments, options, catalogs, and invocation.
8
- - Added owner-aware command resource and runtime capability definitions.
1
+ # Changelog
2
+
3
+ Changes to `@dsh-std/command` are recorded here.
4
+
5
+ ## 0.1.0 (draft)
6
+
7
+ - Defined portable command trees, arguments, options, catalogs, and invocation.
8
+ - Added owner-aware command resource and runtime capability definitions.
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 DSH Standard contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DSH Standard contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,26 +1,26 @@
1
- # @dsh-std/command
2
-
3
- English | [中文](README.zh.md)
4
-
5
- Named human-command resources and their shared callable Runtime dispatcher.
6
-
7
- ## Contract
8
-
9
- A `Command` resource describes one root command and its nested subcommands. Nodes provide a title, localized titles, description, aliases, positional arguments, options, and child nodes. Arguments may be required, variadic, or restricted to documented values. Options declare every literal spelling and may accept one value.
10
-
11
- `CommandRuntime` defines the generic `catalog` and `execute` operations once for every command extension. A runtime adapter joins extensions published by active facets with its authoritative command registry and implements that capability. The execution context id remains opaque to the standard; a DSH adapter maps it to a session. Results contain only the command outcome. Invocation-scoped UI work uses typed clients from `@dsh-std/presentation`.
12
-
13
- `commandRuntime()` wraps a consumer-scoped `CapabilityClient` with typed methods, and `commandRuntimeImplementation()` builds the operation dispatcher for an adapter. Consumers and adapters do not repeat operation strings or payload validation.
14
-
15
- Presentation clients can build completion trees and forms without importing the owning component. A component such as dsh-codex declares `Command/codex`; its selected facet publishes the handler in its local runtime and never defines a connection-specific method.
16
-
17
- Names and aliases are single tokens. Sibling names and aliases, argument names, option spellings, and enumerated values must be unique. A variadic argument must be last.
18
-
19
- ## Extension points
20
-
21
- Fields prefixed with `x-` may carry experimental metadata. Portable clients ignore extensions they do not understand. Incompatible command semantics require a new contract major version rather than redefining an existing field.
22
-
23
- ## Known Limitations and Deferred Work
24
-
25
- - The contract does not define a command-line parser or quoting rules.
26
- - Dynamic completion is not part of `v1alpha1`; declared values are static.
1
+ # @dsh-std/command
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Named human-command resources and their shared callable Runtime dispatcher.
6
+
7
+ ## Contract
8
+
9
+ A `Command` resource describes one root command and its nested subcommands. Nodes provide a title, localized titles, description, aliases, positional arguments, options, and child nodes. Arguments may be required, variadic, or restricted to documented values. Options declare every literal spelling and may accept one value.
10
+
11
+ `CommandRuntime` defines the generic `catalog` and `execute` operations once for every command extension. A runtime adapter joins extensions published by active facets with its authoritative command registry and implements that capability. The execution context id remains opaque to the standard; a DSH adapter maps it to a session. Results contain only the command outcome. Invocation-scoped UI work uses typed clients from `@dsh-std/presentation`.
12
+
13
+ `commandRuntime()` wraps a consumer-scoped `CapabilityClient` with typed methods, and `commandRuntimeImplementation()` builds the operation dispatcher for an adapter. Consumers and adapters do not repeat operation strings or payload validation.
14
+
15
+ Presentation clients can build completion trees and forms without importing the owning component. A component such as dsh-codex declares `Command/codex`; its selected facet publishes the handler in its local runtime and never defines a connection-specific method.
16
+
17
+ Names and aliases are single tokens. Sibling names and aliases, argument names, option spellings, and enumerated values must be unique. A variadic argument must be last.
18
+
19
+ ## Extension points
20
+
21
+ Fields prefixed with `x-` may carry experimental metadata. Portable clients ignore extensions they do not understand. Incompatible command semantics require a new contract major version rather than redefining an existing field.
22
+
23
+ ## Known Limitations and Deferred Work
24
+
25
+ - The contract does not define a command-line parser or quoting rules.
26
+ - Dynamic completion is not part of `v1alpha1`; declared values are static.
@@ -1,28 +1,28 @@
1
- # @dsh-std/command
2
-
3
- [English](README.md) | 中文
4
-
5
- 拟议设计见 [Command Resources and Runtime](../../docs/proposals/command.zh.md)。
6
-
7
- 具名用户命令资源及其共享的可调用 Runtime 分派器。
8
-
9
- ## 协议
10
-
11
- 一项 `Command` 资源描述一个根命令及其嵌套子命令。节点可以提供标题、本地化标题、说明、别名、位置参数、选项和子节点。参数可以是必需、可变长或受已声明值限制;选项声明全部字面拼写,并可接收一个值。
12
-
13
- `CommandRuntime` 为全部命令 extension 统一定义 `catalog` 与 `execute` 操作。运行时适配器把 active facets 发布的 extension 与自身权威命令注册表关联,并实现该能力。执行上下文 id 对标准保持不透明;DSH 适配器将其映射为会话。结果只包含命令结果;调用作用域内的 UI 操作使用 `@dsh-std/presentation` 的类型化 client。
14
-
15
- `commandRuntime()` 用类型化方法包装按消费方隔离的 `CapabilityClient`,`commandRuntimeImplementation()` 为适配器创建操作分派器。消费方与适配器都无需重复操作字符串或载荷校验。
16
-
17
- 表现客户端无需导入所属 component,即可构建补全树和表单。dsh-codex 之类的 component 声明 `Command/codex`,由选中的 facet 在本地运行时发布 handler;它不定义 connection 专用方法。
18
-
19
- 名称与别名必须是单个 token。同级名称与别名、参数名、选项拼写和枚举值必须唯一。可变长参数必须位于末尾。
20
-
21
- ## 扩展点
22
-
23
- 以 `x-` 开头的字段可以承载实验性元数据。可移植客户端忽略无法理解的扩展。不兼容的命令语义需要新的协议主版本,不能重新定义现有字段。
24
-
25
- ## 已知限制与暂缓事项
26
-
27
- - 协议不定义命令行解析器或引号规则。
28
- - `v1alpha1` 不包含动态补全;已声明值是静态的。
1
+ # @dsh-std/command
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 拟议设计见 [Command Resources and Runtime](../../docs/proposals/command.zh.md)。
6
+
7
+ 具名用户命令资源及其共享的可调用 Runtime 分派器。
8
+
9
+ ## 协议
10
+
11
+ 一项 `Command` 资源描述一个根命令及其嵌套子命令。节点可以提供标题、本地化标题、说明、别名、位置参数、选项和子节点。参数可以是必需、可变长或受已声明值限制;选项声明全部字面拼写,并可接收一个值。
12
+
13
+ `CommandRuntime` 为全部命令 extension 统一定义 `catalog` 与 `execute` 操作。运行时适配器把 active facets 发布的 extension 与自身权威命令注册表关联,并实现该能力。执行上下文 id 对标准保持不透明;DSH 适配器将其映射为会话。结果只包含命令结果;调用作用域内的 UI 操作使用 `@dsh-std/presentation` 的类型化 client。
14
+
15
+ `commandRuntime()` 用类型化方法包装按消费方隔离的 `CapabilityClient`,`commandRuntimeImplementation()` 为适配器创建操作分派器。消费方与适配器都无需重复操作字符串或载荷校验。
16
+
17
+ 表现客户端无需导入所属 component,即可构建补全树和表单。dsh-codex 之类的 component 声明 `Command/codex`,由选中的 facet 在本地运行时发布 handler;它不定义 connection 专用方法。
18
+
19
+ 名称与别名必须是单个 token。同级名称与别名、参数名、选项拼写和枚举值必须唯一。可变长参数必须位于末尾。
20
+
21
+ ## 扩展点
22
+
23
+ 以 `x-` 开头的字段可以承载实验性元数据。可移植客户端忽略无法理解的扩展。不兼容的命令语义需要新的协议主版本,不能重新定义现有字段。
24
+
25
+ ## 已知限制与暂缓事项
26
+
27
+ - 协议不定义命令行解析器或引号规则。
28
+ - `v1alpha1` 不包含动态补全;已声明值是静态的。
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n ProtocolCatalog,\n ProtocolDefinition,\n ProtocolIssue,\n ProtocolSupport,\n} from '@dsh-std/core'\nimport type { ManifestDefinitionCatalog, ManifestExtension } from '@dsh-std/manifest'\nimport type {\n CapabilityCall,\n CapabilityClient,\n CapabilityHandlerContext,\n CapabilityImplementation,\n} from '@dsh-std/connection'\nimport { defineCapabilityProtocol } from '@dsh-std/connection'\nimport {\n validatePresentationDescriptor,\n type PresentationClients,\n type PresentationDescriptor,\n} from '@dsh-std/presentation'\n\nexport const API_VERSION = 'commands.dsh/v1alpha1'\nexport const KIND = 'Command'\nexport const RUNTIME_KIND = 'CommandRuntime'\n\n/** Stable reference to one declared command path. */\nexport interface CommandReference {\n readonly name: string\n readonly path?: readonly string[]\n}\n\nexport interface CommandValueSpec {\n readonly value: string\n readonly title?: string\n readonly titles?: Readonly<Record<string, string>>\n}\n\nexport interface CommandArgumentSpec {\n readonly name: string\n readonly title?: string\n readonly titles?: Readonly<Record<string, string>>\n readonly required?: boolean\n readonly variadic?: boolean\n readonly values?: readonly CommandValueSpec[]\n}\n\nexport interface CommandOptionSpec {\n /** Literal spellings such as `--verbose` and `-v`. */\n readonly names: readonly string[]\n readonly title: string\n readonly titles?: Readonly<Record<string, string>>\n readonly value?: CommandArgumentSpec\n}\n\nexport interface CommandNodeSpec {\n readonly title: string\n readonly titles?: Readonly<Record<string, string>>\n readonly description?: string\n readonly aliases?: readonly string[]\n readonly arguments?: readonly CommandArgumentSpec[]\n readonly options?: readonly CommandOptionSpec[]\n readonly children?: readonly CommandNode[]\n}\n\nexport interface CommandNode {\n readonly name: string\n readonly spec: CommandNodeSpec\n}\n\nexport type CommandSpec = CommandNodeSpec\n\nexport type CommandResource = ManifestExtension<CommandSpec>\n\nexport interface CommandOwnerReference {\n readonly component: string\n readonly facet: string\n readonly participantId?: string\n}\n\nexport interface CommandDescriptor {\n readonly name: string\n readonly description: string\n readonly input?: { readonly hint: string }\n readonly owner: CommandOwnerReference\n readonly resource: CommandResource\n readonly available: boolean\n readonly missingPresentation: readonly ProtocolSupport[]\n readonly issues: readonly { readonly code: string; readonly message: string }[]\n}\n\nexport interface CommandCatalog {\n readonly apiVersion: typeof API_VERSION\n readonly commands: readonly CommandDescriptor[]\n}\n\nexport interface CommandCatalogInput {\n /** Opaque execution context selected by the consuming product. */\n readonly contextId: string\n readonly presentation?: PresentationDescriptor\n}\n\nexport interface CommandExecutionInput extends CommandCatalogInput {\n readonly line: string\n}\n\nexport interface CommandExecution {\n readonly apiVersion: typeof API_VERSION\n readonly commandId: string\n readonly result: {\n readonly kind: 'success' | 'error'\n readonly text?: string\n readonly sourceEventSeq?: number\n }\n}\n\nexport type CommandRuntimeCall =\n | { readonly operation: 'catalog'; readonly input: CommandCatalogInput; readonly output: CommandCatalog }\n | { readonly operation: 'execute'; readonly input: CommandExecutionInput; readonly output: CommandExecution | undefined }\n\nexport interface CommandRuntimeClient {\n catalog(input: CommandCatalogInput, options?: { readonly signal?: AbortSignal }): CapabilityCall<CommandCatalog>\n execute(input: CommandExecutionInput, options?: { readonly signal?: AbortSignal }): CapabilityCall<CommandExecution | undefined>\n}\n\nexport interface CommandRuntimeHandler {\n catalog(input: CommandCatalogInput, context: CapabilityHandlerContext): CommandCatalog | Promise<CommandCatalog>\n execute(\n input: CommandExecutionInput,\n context: CapabilityHandlerContext,\n ): CommandExecution | undefined | Promise<CommandExecution | undefined>\n}\n\nexport interface CommandHandlerInput {\n readonly rawInput: string\n}\n\nexport interface CommandHandlerContext {\n readonly signal: AbortSignal\n /** Host-bound clients for Presentation agreements active in this invocation. */\n readonly presentation?: PresentationClients\n}\n\nexport interface CommandHandler {\n execute(\n input: CommandHandlerInput,\n context: CommandHandlerContext,\n ): CommandExecution['result'] | Promise<CommandExecution['result']>\n}\n\nexport function assertCommandHandler(value: unknown): asserts value is CommandHandler {\n if (!record(value) || typeof value.execute !== 'function') {\n throw new TypeError('Command handler must provide execute(input, context)')\n }\n}\n\nexport const extensionDefinition = Object.freeze({\n apiVersion: API_VERSION,\n kind: KIND,\n validateMetadata(metadata: { readonly name: string }): void {\n if (!/^[a-z][a-z0-9_-]*$/u.test(metadata.name)) throw new TypeError('Command metadata.name is invalid')\n },\n validateSpec(value: unknown): void {\n validateNodeSpec(value, 'Command spec')\n },\n})\n\n/** Host support for accepting and owning Command resource publications. */\nexport const resourceProtocol: ProtocolDefinition = Object.freeze({\n apiVersion: API_VERSION,\n kind: KIND,\n validateRequirement: emptyProtocolSpec,\n validateSupport: emptyProtocolSpec,\n negotiate(input) {\n const issues: ProtocolIssue[] = []\n for (const row of input.requirements) {\n if (input.supports.some(candidate => candidate.participant !== row.participant)) continue\n issues.push(Object.freeze({\n code: row.requirement.optional === true ? 'optional-support-missing' : 'required-support-missing',\n severity: row.requirement.optional === true ? 'warning' : 'error',\n participant: row.participant,\n message: `no participant accepts ${row.requirement.apiVersion} ${row.requirement.kind} resources`,\n }))\n }\n return {\n agreement: Object.freeze({ kind: 'ResourcePublication' as const }),\n issues: Object.freeze(issues),\n }\n },\n} satisfies ProtocolDefinition)\n\nexport const resourceSupport: ProtocolSupport = Object.freeze({\n apiVersion: API_VERSION,\n kind: KIND,\n})\n\n/** Callable, resource-keyed command dispatcher implemented once by a Runtime adapter. */\nexport const runtimeProtocol = defineCapabilityProtocol({\n apiVersion: API_VERSION,\n kind: RUNTIME_KIND,\n})\n\nexport const runtimeSupport: ProtocolSupport = Object.freeze({\n apiVersion: API_VERSION,\n kind: RUNTIME_KIND,\n})\n\n/** Bind the typed CommandRuntime operations to a consumer-scoped connection client. */\nexport function commandRuntime(client: CapabilityClient): CommandRuntimeClient {\n return Object.freeze({\n catalog(input: CommandCatalogInput, options?: { readonly signal?: AbortSignal }) {\n return client.invoke<CommandCatalogInput, CommandCatalog>(runtimeSupport, 'catalog', validateCatalogInput(input), options)\n },\n execute(input: CommandExecutionInput, options?: { readonly signal?: AbortSignal }) {\n return client.invoke<CommandExecutionInput, CommandExecution | undefined>(runtimeSupport, 'execute', validateExecutionInput(input), options)\n },\n })\n}\n\n/** Create the sole Runtime-side dispatcher implementation for every Command resource. */\nexport function commandRuntimeImplementation(\n participantId: string,\n handler: CommandRuntimeHandler,\n): CapabilityImplementation {\n return Object.freeze({\n participantId,\n protocol: runtimeSupport,\n handle(operation: string, input: unknown, context: CapabilityHandlerContext) {\n if (operation === 'catalog') return handler.catalog(validateCatalogInput(input), context)\n if (operation === 'execute') return handler.execute(validateExecutionInput(input), context)\n throw new TypeError(`unsupported CommandRuntime operation ${JSON.stringify(operation)}`)\n },\n })\n}\n\nexport function register(protocols: ProtocolCatalog, manifest?: ManifestDefinitionCatalog): () => void {\n const disposeResource = manifest?.registerExtension(extensionDefinition) ?? (() => undefined)\n const disposeResourceProtocol = protocols.register(resourceProtocol)\n const disposeRuntime = protocols.register(runtimeProtocol)\n return () => {\n disposeRuntime()\n disposeResourceProtocol()\n disposeResource()\n }\n}\n\nfunction emptyProtocolSpec(value: unknown): undefined {\n if (value !== undefined) throw new TypeError('Command resource protocol does not accept spec')\n return undefined\n}\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction validateCatalogInput(value: unknown): CommandCatalogInput {\n if (!record(value)) throw new TypeError('CommandRuntime.catalog input must be an object')\n exact(value, ['contextId', 'presentation'], 'CommandRuntime.catalog input')\n text(value.contextId, 'CommandRuntime.catalog input.contextId')\n if (value.presentation !== undefined) validatePresentationDescriptor(value.presentation)\n return Object.freeze({\n contextId: value.contextId as string,\n ...(value.presentation === undefined ? {} : { presentation: value.presentation as PresentationDescriptor }),\n })\n}\n\nfunction validateExecutionInput(value: unknown): CommandExecutionInput {\n if (!record(value)) throw new TypeError('CommandRuntime.execute input must be an object')\n exact(value, ['contextId', 'line', 'presentation'], 'CommandRuntime.execute input')\n const catalog = validateCatalogInput({\n contextId: value.contextId,\n ...(value.presentation === undefined ? {} : { presentation: value.presentation }),\n })\n text(value.line, 'CommandRuntime.execute input.line')\n return Object.freeze({ ...catalog, line: value.line as string })\n}\n\nfunction validateNodeSpec(value: unknown, label: string): void {\n if (!record(value)) throw new TypeError(`${label} must be an object`)\n exact(value, ['title', 'titles', 'description', 'aliases', 'arguments', 'options', 'children'], label)\n text(value.title, `${label}.title`)\n if (value.description !== undefined) text(value.description, `${label}.description`)\n if (value.titles !== undefined) localized(value.titles, `${label}.titles`)\n if (value.aliases !== undefined) tokens(value.aliases, `${label}.aliases`)\n if (value.arguments !== undefined) argumentsList(value.arguments, `${label}.arguments`)\n if (value.options !== undefined) optionsList(value.options, `${label}.options`)\n if (value.children !== undefined) children(value.children, `${label}.children`)\n}\n\nfunction argumentsList(value: unknown, label: string): void {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\n const names = new Set<string>()\n for (const [index, argument] of value.entries()) {\n const item = `${label}[${index}]`\n if (!record(argument)) throw new TypeError(`${item} must be an object`)\n exact(argument, ['name', 'title', 'titles', 'required', 'variadic', 'values'], item)\n token(argument.name, `${item}.name`)\n if (names.has(argument.name as string)) throw new TypeError(`${label} contains a duplicate argument`)\n names.add(argument.name as string)\n if (argument.title !== undefined) text(argument.title, `${item}.title`)\n if (argument.titles !== undefined) localized(argument.titles, `${item}.titles`)\n if (argument.required !== undefined && typeof argument.required !== 'boolean') throw new TypeError(`${item}.required must be boolean`)\n if (argument.variadic !== undefined && typeof argument.variadic !== 'boolean') throw new TypeError(`${item}.variadic must be boolean`)\n if (argument.variadic === true && index !== value.length - 1) throw new TypeError(`${item} must be the last argument when variadic`)\n if (argument.values !== undefined) values(argument.values, `${item}.values`)\n }\n}\n\nfunction optionsList(value: unknown, label: string): void {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\n const names = new Set<string>()\n for (const [index, option] of value.entries()) {\n const item = `${label}[${index}]`\n if (!record(option)) throw new TypeError(`${item} must be an object`)\n exact(option, ['names', 'title', 'titles', 'value'], item)\n text(option.title, `${item}.title`)\n if (option.titles !== undefined) localized(option.titles, `${item}.titles`)\n if (!Array.isArray(option.names) || option.names.length === 0) throw new TypeError(`${item}.names must be a non-empty array`)\n for (const name of option.names) {\n if (typeof name !== 'string' || !/^-{1,2}[^\\s-][^\\s]*$/u.test(name)) throw new TypeError(`${item}.names contains an invalid option`)\n if (names.has(name)) throw new TypeError(`${label} contains a duplicate option name`)\n names.add(name)\n }\n if (option.value !== undefined) argumentsList([option.value], `${item}.value`)\n }\n}\n\nfunction children(value: unknown, label: string): void {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\n const names = new Set<string>()\n for (const [index, child] of value.entries()) {\n const item = `${label}[${index}]`\n if (!record(child)) throw new TypeError(`${item} must be an object`)\n exact(child, ['name', 'spec'], item)\n token(child.name, `${item}.name`)\n for (const name of [child.name, ...(Array.isArray((child.spec as Record<string, unknown> | undefined)?.aliases) ? (child.spec as { aliases: unknown[] }).aliases : [])]) {\n if (typeof name !== 'string') continue\n if (names.has(name)) throw new TypeError(`${label} contains a duplicate name or alias`)\n names.add(name)\n }\n validateNodeSpec(child.spec, `${item}.spec`)\n }\n}\n\nfunction values(value: unknown, label: string): void {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\n const seen = new Set<string>()\n for (const [index, choice] of value.entries()) {\n const item = `${label}[${index}]`\n if (!record(choice)) throw new TypeError(`${item} must be an object`)\n exact(choice, ['value', 'title', 'titles'], item)\n text(choice.value, `${item}.value`)\n if (seen.has(choice.value as string)) throw new TypeError(`${label} contains a duplicate value`)\n seen.add(choice.value as string)\n if (choice.title !== undefined) text(choice.title, `${item}.title`)\n if (choice.titles !== undefined) localized(choice.titles, `${item}.titles`)\n }\n}\n\nfunction localized(value: unknown, label: string): void {\n if (!record(value)) throw new TypeError(`${label} must be an object`)\n for (const [locale, translation] of Object.entries(value)) {\n if (locale.trim() === '') throw new TypeError(`${label} contains an empty locale`)\n text(translation, `${label}.${locale}`)\n }\n}\n\nfunction tokens(value: unknown, label: string): void {\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\n const seen = new Set<string>()\n for (const valueItem of value) {\n token(valueItem, label)\n if (seen.has(valueItem as string)) throw new TypeError(`${label} contains a duplicate token`)\n seen.add(valueItem as string)\n }\n}\n\nfunction token(value: unknown, label: string): void {\n if (typeof value !== 'string' || !/^[^\\s/]+$/u.test(value)) throw new TypeError(`${label} must be one token`)\n}\n\nfunction text(value: unknown, label: string): void {\n if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${label} must be a non-empty string`)\n}\n\nfunction exact(value: Record<string, unknown>, allowed: readonly string[], label: string): void {\n const unknown = Object.keys(value).filter(key => !allowed.includes(key) && !key.startsWith('x-'))\n if (unknown.length > 0) throw new TypeError(`${label} contains unknown field ${JSON.stringify(unknown[0])}`)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\r\n ProtocolCatalog,\r\n ProtocolDefinition,\r\n ProtocolIssue,\r\n ProtocolSupport,\r\n} from '@dsh-std/core'\r\nimport type { ManifestDefinitionCatalog, ManifestExtension } from '@dsh-std/manifest'\r\nimport type {\r\n CapabilityCall,\r\n CapabilityClient,\r\n CapabilityHandlerContext,\r\n CapabilityImplementation,\r\n} from '@dsh-std/connection'\r\nimport { defineCapabilityProtocol } from '@dsh-std/connection'\r\nimport {\r\n validatePresentationDescriptor,\r\n type PresentationClients,\r\n type PresentationDescriptor,\r\n} from '@dsh-std/presentation'\r\n\r\nexport const API_VERSION = 'commands.dsh/v1alpha1'\r\nexport const KIND = 'Command'\r\nexport const RUNTIME_KIND = 'CommandRuntime'\r\n\r\n/** Stable reference to one declared command path. */\r\nexport interface CommandReference {\r\n readonly name: string\r\n readonly path?: readonly string[]\r\n}\r\n\r\nexport interface CommandValueSpec {\r\n readonly value: string\r\n readonly title?: string\r\n readonly titles?: Readonly<Record<string, string>>\r\n}\r\n\r\nexport interface CommandArgumentSpec {\r\n readonly name: string\r\n readonly title?: string\r\n readonly titles?: Readonly<Record<string, string>>\r\n readonly required?: boolean\r\n readonly variadic?: boolean\r\n readonly values?: readonly CommandValueSpec[]\r\n}\r\n\r\nexport interface CommandOptionSpec {\r\n /** Literal spellings such as `--verbose` and `-v`. */\r\n readonly names: readonly string[]\r\n readonly title: string\r\n readonly titles?: Readonly<Record<string, string>>\r\n readonly value?: CommandArgumentSpec\r\n}\r\n\r\nexport interface CommandNodeSpec {\r\n readonly title: string\r\n readonly titles?: Readonly<Record<string, string>>\r\n readonly description?: string\r\n readonly aliases?: readonly string[]\r\n readonly arguments?: readonly CommandArgumentSpec[]\r\n readonly options?: readonly CommandOptionSpec[]\r\n readonly children?: readonly CommandNode[]\r\n}\r\n\r\nexport interface CommandNode {\r\n readonly name: string\r\n readonly spec: CommandNodeSpec\r\n}\r\n\r\nexport type CommandSpec = CommandNodeSpec\r\n\r\nexport type CommandResource = ManifestExtension<CommandSpec>\r\n\r\nexport interface CommandOwnerReference {\r\n readonly component: string\r\n readonly facet: string\r\n readonly participantId?: string\r\n}\r\n\r\nexport interface CommandDescriptor {\r\n readonly name: string\r\n readonly description: string\r\n readonly input?: { readonly hint: string }\r\n readonly owner: CommandOwnerReference\r\n readonly resource: CommandResource\r\n readonly available: boolean\r\n readonly missingPresentation: readonly ProtocolSupport[]\r\n readonly issues: readonly { readonly code: string; readonly message: string }[]\r\n}\r\n\r\nexport interface CommandCatalog {\r\n readonly apiVersion: typeof API_VERSION\r\n readonly commands: readonly CommandDescriptor[]\r\n}\r\n\r\nexport interface CommandCatalogInput {\r\n /** Opaque execution context selected by the consuming product. */\r\n readonly contextId: string\r\n readonly presentation?: PresentationDescriptor\r\n}\r\n\r\nexport interface CommandExecutionInput extends CommandCatalogInput {\r\n readonly line: string\r\n}\r\n\r\nexport interface CommandExecution {\r\n readonly apiVersion: typeof API_VERSION\r\n readonly commandId: string\r\n readonly result: {\r\n readonly kind: 'success' | 'error'\r\n readonly text?: string\r\n readonly sourceEventSeq?: number\r\n }\r\n}\r\n\r\nexport type CommandRuntimeCall =\r\n | { readonly operation: 'catalog'; readonly input: CommandCatalogInput; readonly output: CommandCatalog }\r\n | { readonly operation: 'execute'; readonly input: CommandExecutionInput; readonly output: CommandExecution | undefined }\r\n\r\nexport interface CommandRuntimeClient {\r\n catalog(input: CommandCatalogInput, options?: { readonly signal?: AbortSignal }): CapabilityCall<CommandCatalog>\r\n execute(input: CommandExecutionInput, options?: { readonly signal?: AbortSignal }): CapabilityCall<CommandExecution | undefined>\r\n}\r\n\r\nexport interface CommandRuntimeHandler {\r\n catalog(input: CommandCatalogInput, context: CapabilityHandlerContext): CommandCatalog | Promise<CommandCatalog>\r\n execute(\r\n input: CommandExecutionInput,\r\n context: CapabilityHandlerContext,\r\n ): CommandExecution | undefined | Promise<CommandExecution | undefined>\r\n}\r\n\r\nexport interface CommandHandlerInput {\r\n readonly rawInput: string\r\n}\r\n\r\nexport interface CommandHandlerContext {\r\n readonly signal: AbortSignal\r\n /** Host-bound clients for Presentation agreements active in this invocation. */\r\n readonly presentation?: PresentationClients\r\n}\r\n\r\nexport interface CommandHandler {\r\n execute(\r\n input: CommandHandlerInput,\r\n context: CommandHandlerContext,\r\n ): CommandExecution['result'] | Promise<CommandExecution['result']>\r\n}\r\n\r\nexport function assertCommandHandler(value: unknown): asserts value is CommandHandler {\r\n if (!record(value) || typeof value.execute !== 'function') {\r\n throw new TypeError('Command handler must provide execute(input, context)')\r\n }\r\n}\r\n\r\nexport const extensionDefinition = Object.freeze({\r\n apiVersion: API_VERSION,\r\n kind: KIND,\r\n validateMetadata(metadata: { readonly name: string }): void {\r\n if (!/^[a-z][a-z0-9_-]*$/u.test(metadata.name)) throw new TypeError('Command metadata.name is invalid')\r\n },\r\n validateSpec(value: unknown): void {\r\n validateNodeSpec(value, 'Command spec')\r\n },\r\n})\r\n\r\n/** Host support for accepting and owning Command resource publications. */\r\nexport const resourceProtocol: ProtocolDefinition = Object.freeze({\r\n apiVersion: API_VERSION,\r\n kind: KIND,\r\n validateRequirement: emptyProtocolSpec,\r\n validateSupport: emptyProtocolSpec,\r\n negotiate(input) {\r\n const issues: ProtocolIssue[] = []\r\n for (const row of input.requirements) {\r\n if (input.supports.some(candidate => candidate.participant !== row.participant)) continue\r\n issues.push(Object.freeze({\r\n code: row.requirement.optional === true ? 'optional-support-missing' : 'required-support-missing',\r\n severity: row.requirement.optional === true ? 'warning' : 'error',\r\n participant: row.participant,\r\n message: `no participant accepts ${row.requirement.apiVersion} ${row.requirement.kind} resources`,\r\n }))\r\n }\r\n return {\r\n agreement: Object.freeze({ kind: 'ResourcePublication' as const }),\r\n issues: Object.freeze(issues),\r\n }\r\n },\r\n} satisfies ProtocolDefinition)\r\n\r\nexport const resourceSupport: ProtocolSupport = Object.freeze({\r\n apiVersion: API_VERSION,\r\n kind: KIND,\r\n})\r\n\r\n/** Callable, resource-keyed command dispatcher implemented once by a Runtime adapter. */\r\nexport const runtimeProtocol = defineCapabilityProtocol({\r\n apiVersion: API_VERSION,\r\n kind: RUNTIME_KIND,\r\n})\r\n\r\nexport const runtimeSupport: ProtocolSupport = Object.freeze({\r\n apiVersion: API_VERSION,\r\n kind: RUNTIME_KIND,\r\n})\r\n\r\n/** Bind the typed CommandRuntime operations to a consumer-scoped connection client. */\r\nexport function commandRuntime(client: CapabilityClient): CommandRuntimeClient {\r\n return Object.freeze({\r\n catalog(input: CommandCatalogInput, options?: { readonly signal?: AbortSignal }) {\r\n return client.invoke<CommandCatalogInput, CommandCatalog>(runtimeSupport, 'catalog', validateCatalogInput(input), options)\r\n },\r\n execute(input: CommandExecutionInput, options?: { readonly signal?: AbortSignal }) {\r\n return client.invoke<CommandExecutionInput, CommandExecution | undefined>(runtimeSupport, 'execute', validateExecutionInput(input), options)\r\n },\r\n })\r\n}\r\n\r\n/** Create the sole Runtime-side dispatcher implementation for every Command resource. */\r\nexport function commandRuntimeImplementation(\r\n participantId: string,\r\n handler: CommandRuntimeHandler,\r\n): CapabilityImplementation {\r\n return Object.freeze({\r\n participantId,\r\n protocol: runtimeSupport,\r\n handle(operation: string, input: unknown, context: CapabilityHandlerContext) {\r\n if (operation === 'catalog') return handler.catalog(validateCatalogInput(input), context)\r\n if (operation === 'execute') return handler.execute(validateExecutionInput(input), context)\r\n throw new TypeError(`unsupported CommandRuntime operation ${JSON.stringify(operation)}`)\r\n },\r\n })\r\n}\r\n\r\nexport function register(protocols: ProtocolCatalog, manifest?: ManifestDefinitionCatalog): () => void {\r\n const disposeResource = manifest?.registerExtension(extensionDefinition) ?? (() => undefined)\r\n const disposeResourceProtocol = protocols.register(resourceProtocol)\r\n const disposeRuntime = protocols.register(runtimeProtocol)\r\n return () => {\r\n disposeRuntime()\r\n disposeResourceProtocol()\r\n disposeResource()\r\n }\r\n}\r\n\r\nfunction emptyProtocolSpec(value: unknown): undefined {\r\n if (value !== undefined) throw new TypeError('Command resource protocol does not accept spec')\r\n return undefined\r\n}\r\n\r\nfunction record(value: unknown): value is Record<string, unknown> {\r\n return typeof value === 'object' && value !== null && !Array.isArray(value)\r\n}\r\n\r\nfunction validateCatalogInput(value: unknown): CommandCatalogInput {\r\n if (!record(value)) throw new TypeError('CommandRuntime.catalog input must be an object')\r\n exact(value, ['contextId', 'presentation'], 'CommandRuntime.catalog input')\r\n text(value.contextId, 'CommandRuntime.catalog input.contextId')\r\n if (value.presentation !== undefined) validatePresentationDescriptor(value.presentation)\r\n return Object.freeze({\r\n contextId: value.contextId as string,\r\n ...(value.presentation === undefined ? {} : { presentation: value.presentation as PresentationDescriptor }),\r\n })\r\n}\r\n\r\nfunction validateExecutionInput(value: unknown): CommandExecutionInput {\r\n if (!record(value)) throw new TypeError('CommandRuntime.execute input must be an object')\r\n exact(value, ['contextId', 'line', 'presentation'], 'CommandRuntime.execute input')\r\n const catalog = validateCatalogInput({\r\n contextId: value.contextId,\r\n ...(value.presentation === undefined ? {} : { presentation: value.presentation }),\r\n })\r\n text(value.line, 'CommandRuntime.execute input.line')\r\n return Object.freeze({ ...catalog, line: value.line as string })\r\n}\r\n\r\nfunction validateNodeSpec(value: unknown, label: string): void {\r\n if (!record(value)) throw new TypeError(`${label} must be an object`)\r\n exact(value, ['title', 'titles', 'description', 'aliases', 'arguments', 'options', 'children'], label)\r\n text(value.title, `${label}.title`)\r\n if (value.description !== undefined) text(value.description, `${label}.description`)\r\n if (value.titles !== undefined) localized(value.titles, `${label}.titles`)\r\n if (value.aliases !== undefined) tokens(value.aliases, `${label}.aliases`)\r\n if (value.arguments !== undefined) argumentsList(value.arguments, `${label}.arguments`)\r\n if (value.options !== undefined) optionsList(value.options, `${label}.options`)\r\n if (value.children !== undefined) children(value.children, `${label}.children`)\r\n}\r\n\r\nfunction argumentsList(value: unknown, label: string): void {\r\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\r\n const names = new Set<string>()\r\n for (const [index, argument] of value.entries()) {\r\n const item = `${label}[${index}]`\r\n if (!record(argument)) throw new TypeError(`${item} must be an object`)\r\n exact(argument, ['name', 'title', 'titles', 'required', 'variadic', 'values'], item)\r\n token(argument.name, `${item}.name`)\r\n if (names.has(argument.name as string)) throw new TypeError(`${label} contains a duplicate argument`)\r\n names.add(argument.name as string)\r\n if (argument.title !== undefined) text(argument.title, `${item}.title`)\r\n if (argument.titles !== undefined) localized(argument.titles, `${item}.titles`)\r\n if (argument.required !== undefined && typeof argument.required !== 'boolean') throw new TypeError(`${item}.required must be boolean`)\r\n if (argument.variadic !== undefined && typeof argument.variadic !== 'boolean') throw new TypeError(`${item}.variadic must be boolean`)\r\n if (argument.variadic === true && index !== value.length - 1) throw new TypeError(`${item} must be the last argument when variadic`)\r\n if (argument.values !== undefined) values(argument.values, `${item}.values`)\r\n }\r\n}\r\n\r\nfunction optionsList(value: unknown, label: string): void {\r\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\r\n const names = new Set<string>()\r\n for (const [index, option] of value.entries()) {\r\n const item = `${label}[${index}]`\r\n if (!record(option)) throw new TypeError(`${item} must be an object`)\r\n exact(option, ['names', 'title', 'titles', 'value'], item)\r\n text(option.title, `${item}.title`)\r\n if (option.titles !== undefined) localized(option.titles, `${item}.titles`)\r\n if (!Array.isArray(option.names) || option.names.length === 0) throw new TypeError(`${item}.names must be a non-empty array`)\r\n for (const name of option.names) {\r\n if (typeof name !== 'string' || !/^-{1,2}[^\\s-][^\\s]*$/u.test(name)) throw new TypeError(`${item}.names contains an invalid option`)\r\n if (names.has(name)) throw new TypeError(`${label} contains a duplicate option name`)\r\n names.add(name)\r\n }\r\n if (option.value !== undefined) argumentsList([option.value], `${item}.value`)\r\n }\r\n}\r\n\r\nfunction children(value: unknown, label: string): void {\r\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\r\n const names = new Set<string>()\r\n for (const [index, child] of value.entries()) {\r\n const item = `${label}[${index}]`\r\n if (!record(child)) throw new TypeError(`${item} must be an object`)\r\n exact(child, ['name', 'spec'], item)\r\n token(child.name, `${item}.name`)\r\n for (const name of [child.name, ...(Array.isArray((child.spec as Record<string, unknown> | undefined)?.aliases) ? (child.spec as { aliases: unknown[] }).aliases : [])]) {\r\n if (typeof name !== 'string') continue\r\n if (names.has(name)) throw new TypeError(`${label} contains a duplicate name or alias`)\r\n names.add(name)\r\n }\r\n validateNodeSpec(child.spec, `${item}.spec`)\r\n }\r\n}\r\n\r\nfunction values(value: unknown, label: string): void {\r\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\r\n const seen = new Set<string>()\r\n for (const [index, choice] of value.entries()) {\r\n const item = `${label}[${index}]`\r\n if (!record(choice)) throw new TypeError(`${item} must be an object`)\r\n exact(choice, ['value', 'title', 'titles'], item)\r\n text(choice.value, `${item}.value`)\r\n if (seen.has(choice.value as string)) throw new TypeError(`${label} contains a duplicate value`)\r\n seen.add(choice.value as string)\r\n if (choice.title !== undefined) text(choice.title, `${item}.title`)\r\n if (choice.titles !== undefined) localized(choice.titles, `${item}.titles`)\r\n }\r\n}\r\n\r\nfunction localized(value: unknown, label: string): void {\r\n if (!record(value)) throw new TypeError(`${label} must be an object`)\r\n for (const [locale, translation] of Object.entries(value)) {\r\n if (locale.trim() === '') throw new TypeError(`${label} contains an empty locale`)\r\n text(translation, `${label}.${locale}`)\r\n }\r\n}\r\n\r\nfunction tokens(value: unknown, label: string): void {\r\n if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`)\r\n const seen = new Set<string>()\r\n for (const valueItem of value) {\r\n token(valueItem, label)\r\n if (seen.has(valueItem as string)) throw new TypeError(`${label} contains a duplicate token`)\r\n seen.add(valueItem as string)\r\n }\r\n}\r\n\r\nfunction token(value: unknown, label: string): void {\r\n if (typeof value !== 'string' || !/^[^\\s/]+$/u.test(value)) throw new TypeError(`${label} must be one token`)\r\n}\r\n\r\nfunction text(value: unknown, label: string): void {\r\n if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${label} must be a non-empty string`)\r\n}\r\n\r\nfunction exact(value: Record<string, unknown>, allowed: readonly string[], label: string): void {\r\n const unknown = Object.keys(value).filter(key => !allowed.includes(key) && !key.startsWith('x-'))\r\n if (unknown.length > 0) throw new TypeError(`${label} contains unknown field ${JSON.stringify(unknown[0])}`)\r\n}\r\n"],"mappings":"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"}