martty 0.2.17 → 0.2.18
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.
- package/lib/acp-host.js +23 -11
- package/lib/ayu.js +22 -0
- package/lib/boot.js +26 -4
- package/lib/catppuccin.js +23 -0
- package/lib/client-run.js +11 -2
- package/lib/cordis-protocol.js +4 -0
- package/lib/creator-overlay.js +3 -3
- package/lib/everforest.js +21 -0
- package/lib/iceberg.js +21 -0
- package/lib/index.js +10 -2
- package/lib/inspect.js +139 -7
- package/lib/kanagawa.js +22 -0
- package/lib/mux.js +2 -0
- package/lib/palettes/ayu.json +44 -0
- package/lib/palettes/catppuccin.json +44 -0
- package/lib/palettes/everforest.json +44 -0
- package/lib/palettes/iceberg.json +44 -0
- package/lib/palettes/kanagawa.json +44 -0
- package/lib/palettes/solarized.json +44 -0
- package/lib/runner.js +2 -2
- package/lib/solarized.js +21 -0
- package/lib/tui-client-plugin-registry.js +3 -3
- package/lib/tui-local-plugins.js +2 -1
- package/lib/tui-plugin-store.js +4 -3
- package/lib/tui-presets.js +129 -13
- package/lib/tui-theme.js +17 -3
- package/package.json +4 -3
- package/skills/tui-plugin-development/SKILL.md +11 -8
- package/vendor/darwin-arm64/dsh-tui +0 -0
- package/vendor/darwin-x64/dsh-tui +0 -0
- package/vendor/linux-arm64/dsh-tui +0 -0
- package/vendor/linux-x64/dsh-tui +0 -0
- package/vendor/win32-x64/dsh-tui.exe +0 -0
package/lib/acp-host.js
CHANGED
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
/** Resolve the embeddable ACP plugin from TUI's own dependency graph. */
|
|
2
2
|
|
|
3
3
|
import { createRequire } from 'node:module'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
4
5
|
|
|
5
6
|
export const name = 'dsh-tui-acp-host'
|
|
6
7
|
export const inject = ['loader']
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return specifier
|
|
9
|
+
function resolvedModule(ctx, specifier) {
|
|
10
|
+
for (const anchor of [ctx.baseUrl, import.meta.url]) {
|
|
11
|
+
if (typeof anchor !== 'string') continue
|
|
12
|
+
try {
|
|
13
|
+
return pathToFileURL(createRequire(anchor).resolve(specifier)).href
|
|
14
|
+
} catch {
|
|
15
|
+
// Try TUI's dependency graph after the active profile.
|
|
16
|
+
}
|
|
17
17
|
}
|
|
18
|
+
return specifier
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export async function apply(ctx, config) {
|
|
21
|
-
const
|
|
22
|
+
const specifier = '@openma/deepseek-harness-acp/plugin'
|
|
23
|
+
const resolved = resolvedModule(ctx, specifier)
|
|
24
|
+
let exports
|
|
25
|
+
try {
|
|
26
|
+
exports = await import(resolved)
|
|
27
|
+
} catch (cause) {
|
|
28
|
+
throw new Error(`dsh-tui: failed to import ${specifier} from ${resolved}`, { cause })
|
|
29
|
+
}
|
|
22
30
|
const plugin = ctx.loader.unwrapExports(exports)
|
|
23
|
-
|
|
31
|
+
try {
|
|
32
|
+
await ctx.plugin(plugin, config)
|
|
33
|
+
} catch (cause) {
|
|
34
|
+
throw new Error(`dsh-tui: failed to mount ${specifier}`, { cause })
|
|
35
|
+
}
|
|
24
36
|
}
|
package/lib/ayu.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery palette pack `ayu`. Registers complete token maps: dark from the
|
|
3
|
+
* Ayu dark variant, light from Ayu Light
|
|
4
|
+
* (terminalcolors.com/themes/ayu). Does not activate:
|
|
5
|
+
* `/theme` covers it. `inject = ['tuiTheme']`: sibling profile row, not
|
|
6
|
+
* `ctx.plugin` inside the runner.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from 'node:fs'
|
|
10
|
+
|
|
11
|
+
const ayuPalette = JSON.parse(
|
|
12
|
+
readFileSync(new URL('./palettes/ayu.json', import.meta.url), 'utf8'),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
export const name = 'tui-theme-ayu'
|
|
16
|
+
export const inject = ['tuiTheme']
|
|
17
|
+
|
|
18
|
+
export function apply(ctx) {
|
|
19
|
+
ctx.effect(() => ctx.tuiTheme.register(ayuPalette, { activate: false }))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { ayuPalette }
|
package/lib/boot.js
CHANGED
|
@@ -15,6 +15,12 @@ import { apply as applyShell } from './index.js'
|
|
|
15
15
|
import { resolveStackedAgent } from './agent.js'
|
|
16
16
|
import { apply as applySlots } from './tui-slots.js'
|
|
17
17
|
import { apply as applyTheme } from './tui-theme.js'
|
|
18
|
+
import { apply as applyAyu, inject as ayuInject } from './ayu.js'
|
|
19
|
+
import { apply as applyCatppuccin, inject as catppuccinInject } from './catppuccin.js'
|
|
20
|
+
import { apply as applyKanagawa, inject as kanagawaInject } from './kanagawa.js'
|
|
21
|
+
import { apply as applyEverforest, inject as everforestInject } from './everforest.js'
|
|
22
|
+
import { apply as applyIceberg, inject as icebergInject } from './iceberg.js'
|
|
23
|
+
import { apply as applySolarized, inject as solarizedInject } from './solarized.js'
|
|
18
24
|
import { apply as applyCommands } from './tui-commands.js'
|
|
19
25
|
import { apply as applyOverlay } from './tui-overlay.js'
|
|
20
26
|
import { apply as applyPresets, inject as presetsInject } from './tui-presets.js'
|
|
@@ -34,7 +40,7 @@ import { installTuiLocalPlugins } from './tui-local-plugins.js'
|
|
|
34
40
|
* @param {{ stdin: number | 'inherit', stdout: number | 'inherit' }} [options.tty]
|
|
35
41
|
* @param {string} [options.settingsPath]
|
|
36
42
|
* @param {string} [options.artifactRoot]
|
|
37
|
-
* @param {Array<{ id: string, kind: 'theme' | 'ui
|
|
43
|
+
* @param {Array<{ id: string, kind: 'theme' | 'ui', entry: string }>} [options.packagePlugins]
|
|
38
44
|
*/
|
|
39
45
|
export async function bootClient(options = {}) {
|
|
40
46
|
const { Context } = await import('@deepseek-ai/cordis')
|
|
@@ -49,6 +55,12 @@ export async function bootClient(options = {}) {
|
|
|
49
55
|
const presetConfig = { settingsPath }
|
|
50
56
|
if (typeof ctx.plugin === 'function') {
|
|
51
57
|
await ctx.plugin({ name: 'tui-theme', inject: [], apply: applyTheme }, presetConfig)
|
|
58
|
+
await ctx.plugin({ name: 'tui-theme-ayu', inject: ayuInject, apply: applyAyu })
|
|
59
|
+
await ctx.plugin({ name: 'tui-theme-catppuccin', inject: catppuccinInject, apply: applyCatppuccin })
|
|
60
|
+
await ctx.plugin({ name: 'tui-theme-kanagawa', inject: kanagawaInject, apply: applyKanagawa })
|
|
61
|
+
await ctx.plugin({ name: 'tui-theme-everforest', inject: everforestInject, apply: applyEverforest })
|
|
62
|
+
await ctx.plugin({ name: 'tui-theme-iceberg', inject: icebergInject, apply: applyIceberg })
|
|
63
|
+
await ctx.plugin({ name: 'tui-theme-solarized', inject: solarizedInject, apply: applySolarized })
|
|
52
64
|
await ctx.plugin({ name: 'tui-slots', inject: [], apply: applySlots })
|
|
53
65
|
await ctx.plugin({ name: 'tui-commands', inject: [], apply: applyCommands })
|
|
54
66
|
await ctx.plugin({ name: 'tui-overlay', inject: [], apply: applyOverlay })
|
|
@@ -87,7 +99,7 @@ export async function bootClient(options = {}) {
|
|
|
87
99
|
name: 'dsh-tui-shell',
|
|
88
100
|
inject: [
|
|
89
101
|
'acpClient', 'tuiTheme', 'tuiSlots', 'tuiCommands', 'tuiOverlay',
|
|
90
|
-
'acpClientEvents', 'acpSessionConfig', 'tuiCordisClientRunner',
|
|
102
|
+
'tuiPresets', 'acpClientEvents', 'acpSessionConfig', 'tuiCordisClientRunner',
|
|
91
103
|
],
|
|
92
104
|
apply: applyShell,
|
|
93
105
|
},
|
|
@@ -95,6 +107,12 @@ export async function bootClient(options = {}) {
|
|
|
95
107
|
)
|
|
96
108
|
} else {
|
|
97
109
|
applyTheme(ctx, presetConfig)
|
|
110
|
+
applyAyu(ctx)
|
|
111
|
+
applyCatppuccin(ctx)
|
|
112
|
+
applyKanagawa(ctx)
|
|
113
|
+
applyEverforest(ctx)
|
|
114
|
+
applyIceberg(ctx)
|
|
115
|
+
applySolarized(ctx)
|
|
98
116
|
applySlots(ctx)
|
|
99
117
|
applyCommands(ctx)
|
|
100
118
|
applyOverlay(ctx)
|
|
@@ -145,7 +163,7 @@ export function parseClientPluginsEnv(value) {
|
|
|
145
163
|
if (typeof entry.id !== 'string' || !/^[a-z0-9][a-z0-9-]*$/.test(entry.id)) {
|
|
146
164
|
throw new Error(`dsh-tui: installed Client plugin entry ${index} has an invalid id`)
|
|
147
165
|
}
|
|
148
|
-
if (!['theme', 'ui-preset'].includes(entry.kind)) {
|
|
166
|
+
if (!['theme', 'ui', 'ui-preset'].includes(entry.kind)) {
|
|
149
167
|
throw new Error(`dsh-tui: installed Client plugin entry ${index} has an invalid kind`)
|
|
150
168
|
}
|
|
151
169
|
if (typeof entry.entry !== 'string') {
|
|
@@ -160,7 +178,11 @@ export function parseClientPluginsEnv(value) {
|
|
|
160
178
|
if (url.protocol !== 'file:') {
|
|
161
179
|
throw new Error(`dsh-tui: installed Client plugin entry ${index} must use a file URL`)
|
|
162
180
|
}
|
|
163
|
-
return {
|
|
181
|
+
return {
|
|
182
|
+
id: entry.id,
|
|
183
|
+
kind: entry.kind === 'ui-preset' ? 'ui' : entry.kind,
|
|
184
|
+
entry: url.href,
|
|
185
|
+
}
|
|
164
186
|
})
|
|
165
187
|
}
|
|
166
188
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery palette pack `catppuccin`. Registers complete token maps: dark
|
|
3
|
+
* from the Catppuccin Mocha variant, light from Catppuccin Latte
|
|
4
|
+
* (terminalcolors.com/themes/catppuccin)
|
|
5
|
+
* (terminalcolors.com/themes/catppuccin). Does not activate: `/theme`
|
|
6
|
+
* covers it. `inject = ['tuiTheme']`: sibling profile row, not `ctx.plugin`
|
|
7
|
+
* inside the runner.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'node:fs'
|
|
11
|
+
|
|
12
|
+
const catppuccinPalette = JSON.parse(
|
|
13
|
+
readFileSync(new URL('./palettes/catppuccin.json', import.meta.url), 'utf8'),
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
export const name = 'tui-theme-catppuccin'
|
|
17
|
+
export const inject = ['tuiTheme']
|
|
18
|
+
|
|
19
|
+
export function apply(ctx) {
|
|
20
|
+
ctx.effect(() => ctx.tuiTheme.register(catppuccinPalette, { activate: false }))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { catppuccinPalette }
|
package/lib/client-run.js
CHANGED
|
@@ -161,7 +161,10 @@ function restrictedCtx(env, own, inject) {
|
|
|
161
161
|
&& typeof sourceTheme.registerOwned === 'function'
|
|
162
162
|
? sourceTheme.registerOwned.bind(sourceTheme, env.pluginId)
|
|
163
163
|
: sourceTheme.register.bind(sourceTheme)
|
|
164
|
-
const registration = register(palette,
|
|
164
|
+
const registration = register(palette, {
|
|
165
|
+
...options,
|
|
166
|
+
...(typeof env.pluginSource === 'string' ? { source: env.pluginSource } : {}),
|
|
167
|
+
})
|
|
165
168
|
const dispose = own(
|
|
166
169
|
typeof registration?.dispose === 'function'
|
|
167
170
|
? registration.dispose.bind(registration)
|
|
@@ -199,7 +202,13 @@ function restrictedCtx(env, own, inject) {
|
|
|
199
202
|
? undefined
|
|
200
203
|
: {
|
|
201
204
|
register(options, mount) {
|
|
202
|
-
|
|
205
|
+
const register = typeof env.pluginId === 'string'
|
|
206
|
+
&& typeof sourcePresets.registerOwned === 'function'
|
|
207
|
+
? sourcePresets.registerOwned.bind(sourcePresets, env.pluginId)
|
|
208
|
+
: sourcePresets.register.bind(sourcePresets)
|
|
209
|
+
return own(register(options, mount, {
|
|
210
|
+
...(typeof env.pluginSource === 'string' ? { source: env.pluginSource } : {}),
|
|
211
|
+
}))
|
|
203
212
|
},
|
|
204
213
|
list() {
|
|
205
214
|
return sourcePresets.list()
|
package/lib/cordis-protocol.js
CHANGED
|
@@ -21,6 +21,10 @@ export const CORDIS_METHODS = Object.freeze({
|
|
|
21
21
|
pluginStart: '_dsh/cordis/plugins/start',
|
|
22
22
|
pluginStop: '_dsh/cordis/plugins/stop',
|
|
23
23
|
pluginRetract: '_dsh/cordis/plugins/retract',
|
|
24
|
+
approvalsUpdate: '_dsh/cordis/tui/approvals/update',
|
|
25
|
+
approvalRespond: '_dsh/cordis/tui/approvals/respond',
|
|
26
|
+
uiUpdate: '_dsh/cordis/tui/ui/update',
|
|
27
|
+
uiSelected: '_dsh/cordis/tui/ui/selected',
|
|
24
28
|
themeUpdate: '_dsh/cordis/tui/theme/update',
|
|
25
29
|
themeRemove: '_dsh/cordis/tui/theme/remove',
|
|
26
30
|
themeSelected: '_dsh/cordis/tui/theme/selected',
|
package/lib/creator-overlay.js
CHANGED
|
@@ -41,7 +41,7 @@ function registerArtifactTools(ctx, scopedCtx, defineTool, store) {
|
|
|
41
41
|
name: 'tui_plugin_save',
|
|
42
42
|
description:
|
|
43
43
|
'Persist one successfully activated, Client-only Cordis Package as a durable TUI artifact. '
|
|
44
|
-
+ 'Use this only after cordis_run succeeds. UI
|
|
44
|
+
+ 'Use this only after cordis_run succeeds. UI and Theme Plugins survive restart only '
|
|
45
45
|
+ 'after this Tool returns saved. Replacing an existing artifact must be explicit.',
|
|
46
46
|
parameters: {
|
|
47
47
|
artifactId: {
|
|
@@ -52,7 +52,7 @@ function registerArtifactTools(ctx, scopedCtx, defineTool, store) {
|
|
|
52
52
|
kind: {
|
|
53
53
|
type: 'string',
|
|
54
54
|
required: true,
|
|
55
|
-
enum: ['ui
|
|
55
|
+
enum: ['ui', 'theme'],
|
|
56
56
|
description: 'Durable TUI contribution kind.',
|
|
57
57
|
},
|
|
58
58
|
pluginId: {
|
|
@@ -86,7 +86,7 @@ function registerArtifactTools(ctx, scopedCtx, defineTool, store) {
|
|
|
86
86
|
if (typeof source.code?.host === 'string') {
|
|
87
87
|
throw new Error(
|
|
88
88
|
'tui_plugin_save: durable TUI artifacts are client-only; a Package with a Host half '
|
|
89
|
-
+ '
|
|
89
|
+
+ 'belongs to the connected Harness and needs its separate shipping path',
|
|
90
90
|
)
|
|
91
91
|
}
|
|
92
92
|
if (typeof source.code?.client !== 'string') {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery palette pack `everforest`. Registers complete dark/light token maps:
|
|
3
|
+
* dark from Everforest Dark, light from Everforest Light (terminalcolors.com/themes/everforest). Does not activate:
|
|
4
|
+
* `/theme` covers it. `inject = ['tuiTheme']`: sibling profile row, not
|
|
5
|
+
* `ctx.plugin` inside the runner.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'node:fs'
|
|
9
|
+
|
|
10
|
+
const everforestPalette = JSON.parse(
|
|
11
|
+
readFileSync(new URL('./palettes/everforest.json', import.meta.url), 'utf8'),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export const name = 'tui-theme-everforest'
|
|
15
|
+
export const inject = ['tuiTheme']
|
|
16
|
+
|
|
17
|
+
export function apply(ctx) {
|
|
18
|
+
ctx.effect(() => ctx.tuiTheme.register(everforestPalette, { activate: false }))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { everforestPalette }
|
package/lib/iceberg.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery palette pack `iceberg`. Registers complete dark/light token maps:
|
|
3
|
+
* dark from Iceberg Dark, light from Iceberg Light (terminalcolors.com/themes/iceberg). Does not activate:
|
|
4
|
+
* `/theme` covers it. `inject = ['tuiTheme']`: sibling profile row, not
|
|
5
|
+
* `ctx.plugin` inside the runner.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'node:fs'
|
|
9
|
+
|
|
10
|
+
const icebergPalette = JSON.parse(
|
|
11
|
+
readFileSync(new URL('./palettes/iceberg.json', import.meta.url), 'utf8'),
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export const name = 'tui-theme-iceberg'
|
|
15
|
+
export const inject = ['tuiTheme']
|
|
16
|
+
|
|
17
|
+
export function apply(ctx) {
|
|
18
|
+
ctx.effect(() => ctx.tuiTheme.register(icebergPalette, { activate: false }))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { icebergPalette }
|
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { muxAcpAndCompositor } from './mux.js'
|
|
|
16
16
|
|
|
17
17
|
export const name = 'dsh-tui-shell'
|
|
18
18
|
export const inject = [
|
|
19
|
-
'acpClient', 'tuiTheme', 'tuiSlots', 'tuiCommands', 'tuiOverlay',
|
|
19
|
+
'acpClient', 'tuiTheme', 'tuiPresets', 'tuiSlots', 'tuiCommands', 'tuiOverlay',
|
|
20
20
|
'acpClientEvents', 'acpSessionConfig', 'tuiCordisClientRunner',
|
|
21
21
|
]
|
|
22
22
|
|
|
@@ -115,6 +115,7 @@ export async function applyShell(ctx, options = {}) {
|
|
|
115
115
|
throw new Error('dsh-tui-shell: ctx.acpClient must expose stdin and stdout')
|
|
116
116
|
}
|
|
117
117
|
const clientRunner = ctx.tuiCordisClientRunner ?? ctx.get?.('tuiCordisClientRunner')
|
|
118
|
+
const presets = ctx.tuiPresets ?? ctx.get?.('tuiPresets')
|
|
118
119
|
if (clientRunner === undefined || typeof clientRunner.bindTransport !== 'function'
|
|
119
120
|
|| typeof clientRunner.selectTheme !== 'function') {
|
|
120
121
|
throw new Error(
|
|
@@ -225,18 +226,25 @@ export async function applyShell(ctx, options = {}) {
|
|
|
225
226
|
if (message.method === CORDIS_METHODS.overlayEvent) {
|
|
226
227
|
return overlay.dispatch(message.params)
|
|
227
228
|
}
|
|
229
|
+
if (message.method === CORDIS_METHODS.approvalRespond) {
|
|
230
|
+
return clientRunner.respondApproval(message.params)
|
|
231
|
+
}
|
|
232
|
+
if (message.method === CORDIS_METHODS.uiSelected) {
|
|
233
|
+
return clientRunner.selectUi(message.params)
|
|
234
|
+
}
|
|
228
235
|
throw new Error(`unsupported Cordis TUI method: ${String(message.method)}`)
|
|
229
236
|
},
|
|
230
237
|
})
|
|
231
238
|
const notifyTui = (method, params) => mux.notifyTui(method, params)
|
|
232
239
|
republishCompositorState = () => {
|
|
233
240
|
handle.bindNotify(notifyTui)
|
|
241
|
+
presets?.bindNotify?.(notifyTui)
|
|
234
242
|
slots.bindNotify(notifyTui)
|
|
235
243
|
commands.bindNotify(notifyTui)
|
|
236
244
|
overlay.bindNotify(notifyTui)
|
|
237
245
|
}
|
|
238
246
|
republishCompositorState()
|
|
239
|
-
clientRunner.bindTransport(mux.requestAgent)
|
|
247
|
+
clientRunner.bindTransport(mux.requestAgent, notifyTui)
|
|
240
248
|
sessionConfig.bindTransport(mux.requestTui)
|
|
241
249
|
connection.resume?.()
|
|
242
250
|
connection.muxed = true
|
package/lib/inspect.js
CHANGED
|
@@ -756,14 +756,15 @@ async function mountClientHalf(
|
|
|
756
756
|
* acpSessionStatus?: object,
|
|
757
757
|
* localPlugins?: object,
|
|
758
758
|
* requestAgent: (method: string, params?: object) => Promise<unknown>,
|
|
759
|
+
* notifyTui?: (method: string, params?: object) => void,
|
|
759
760
|
* }} opts
|
|
760
|
-
* @returns {{ onHost: (message: object) => void }}
|
|
761
|
+
* @returns {{ onHost: (message: object) => void, respondApproval: (params: object) => Promise<void> }}
|
|
761
762
|
*/
|
|
762
763
|
export function attachTuiClient(opts) {
|
|
763
764
|
const {
|
|
764
765
|
ctx, tuiTheme, tuiPresets, tuiSlots, tuiCommands, tuiOverlay, acpSessionConfig, acpSessionPlan,
|
|
765
766
|
acpSessionStats, acpSessionStatus, localPlugins,
|
|
766
|
-
requestAgent,
|
|
767
|
+
requestAgent, notifyTui = () => {},
|
|
767
768
|
} = opts
|
|
768
769
|
const providers = [
|
|
769
770
|
themeInspectProvider(tuiTheme),
|
|
@@ -780,6 +781,23 @@ export function attachTuiClient(opts) {
|
|
|
780
781
|
/** @type {Map<string, () => void>} */
|
|
781
782
|
const loaded = new Map()
|
|
782
783
|
const pendingThemeSelections = new Map()
|
|
784
|
+
const pendingUiSelections = new Map()
|
|
785
|
+
const pendingApprovals = new Map()
|
|
786
|
+
|
|
787
|
+
function publishApprovals() {
|
|
788
|
+
notifyTui(CORDIS_METHODS.approvalsUpdate, {
|
|
789
|
+
protocol: 0,
|
|
790
|
+
approvals: [...pendingApprovals.values()].map((request) => ({
|
|
791
|
+
requestId: request.requestId,
|
|
792
|
+
agentId: request.agentId,
|
|
793
|
+
pluginId: request.pluginId,
|
|
794
|
+
packageId: request.packageId,
|
|
795
|
+
mode: request.mode,
|
|
796
|
+
name: request.name,
|
|
797
|
+
purpose: request.purpose,
|
|
798
|
+
})),
|
|
799
|
+
})
|
|
800
|
+
}
|
|
783
801
|
|
|
784
802
|
async function stopOwner(agentId, pluginId) {
|
|
785
803
|
if (localPlugins?.has?.(pluginId) === true) {
|
|
@@ -828,7 +846,7 @@ export function attachTuiClient(opts) {
|
|
|
828
846
|
await requestAgent(CORDIS_METHODS.inspectResolve, { agentId, requestId, resolution }).catch(() => {})
|
|
829
847
|
}
|
|
830
848
|
|
|
831
|
-
async function runClient(params, direct = false) {
|
|
849
|
+
async function runClient(params, direct = false, approveFutureVersions = false) {
|
|
832
850
|
const request = params !== null && typeof params === 'object' ? params : {}
|
|
833
851
|
const requestId = request.requestId
|
|
834
852
|
if (!direct && typeof requestId !== 'string') return
|
|
@@ -842,6 +860,7 @@ export function attachTuiClient(opts) {
|
|
|
842
860
|
? { ok: true, pluginRunId: request.pluginRunId }
|
|
843
861
|
: undefined
|
|
844
862
|
const previousThemeOwner = tuiTheme.owner?.(tuiTheme.active())
|
|
863
|
+
const previousUiOwner = tuiPresets?.owner?.(tuiPresets.active?.())
|
|
845
864
|
try {
|
|
846
865
|
if (!direct) {
|
|
847
866
|
host = await requestAgent(CORDIS_METHODS.runHost, {
|
|
@@ -850,7 +869,7 @@ export function attachTuiClient(opts) {
|
|
|
850
869
|
packageId: request.packageId,
|
|
851
870
|
mode: request.mode,
|
|
852
871
|
requestId,
|
|
853
|
-
approveFutureVersions
|
|
872
|
+
approveFutureVersions,
|
|
854
873
|
})
|
|
855
874
|
}
|
|
856
875
|
if (host === null || typeof host !== 'object' || host.ok !== true) {
|
|
@@ -909,6 +928,15 @@ export function attachTuiClient(opts) {
|
|
|
909
928
|
},
|
|
910
929
|
)
|
|
911
930
|
loaded.set(request.pluginId, applied.dispose)
|
|
931
|
+
const selectedUi = pendingUiSelections.get(request.pluginId)
|
|
932
|
+
if (selectedUi !== undefined && tuiPresets?.isLoaded?.(selectedUi) === true) {
|
|
933
|
+
pendingUiSelections.delete(request.pluginId)
|
|
934
|
+
tuiPresets.activate(selectedUi)
|
|
935
|
+
if (previousUiOwner !== undefined && previousUiOwner !== request.pluginId
|
|
936
|
+
&& tuiTheme.owner?.(tuiTheme.active()) !== previousUiOwner) {
|
|
937
|
+
await stopOwner(request.agentId, previousUiOwner)
|
|
938
|
+
}
|
|
939
|
+
}
|
|
912
940
|
const selectedTheme = pendingThemeSelections.get(request.pluginId)
|
|
913
941
|
if (selectedTheme !== undefined && tuiTheme.isLoaded?.(selectedTheme) === true) {
|
|
914
942
|
pendingThemeSelections.delete(request.pluginId)
|
|
@@ -953,7 +981,21 @@ export function attachTuiClient(opts) {
|
|
|
953
981
|
case CORDIS_METHODS.inspectQuery:
|
|
954
982
|
return answerQuery(message.params)
|
|
955
983
|
case CORDIS_METHODS.requestRun:
|
|
984
|
+
if (message.params !== null && typeof message.params === 'object'
|
|
985
|
+
&& message.params.requiresApproval === true
|
|
986
|
+
&& typeof message.params.requestId === 'string') {
|
|
987
|
+
pendingApprovals.set(message.params.requestId, { ...message.params })
|
|
988
|
+
publishApprovals()
|
|
989
|
+
return
|
|
990
|
+
}
|
|
956
991
|
return runClient(message.params)
|
|
992
|
+
case CORDIS_METHODS.requestRunResolved: {
|
|
993
|
+
const requestId = message.params !== null && typeof message.params === 'object'
|
|
994
|
+
? message.params.requestId
|
|
995
|
+
: undefined
|
|
996
|
+
if (typeof requestId === 'string' && pendingApprovals.delete(requestId)) publishApprovals()
|
|
997
|
+
return
|
|
998
|
+
}
|
|
957
999
|
case CORDIS_METHODS.userRun:
|
|
958
1000
|
return runClient(message.params, true)
|
|
959
1001
|
case CORDIS_METHODS.pluginRetract: {
|
|
@@ -970,6 +1012,29 @@ export function attachTuiClient(opts) {
|
|
|
970
1012
|
}
|
|
971
1013
|
}
|
|
972
1014
|
|
|
1015
|
+
async function respondApproval(params) {
|
|
1016
|
+
const request = params !== null && typeof params === 'object' ? params : {}
|
|
1017
|
+
if (request.protocol !== 0) throw new Error('unsupported approval response payload')
|
|
1018
|
+
if (typeof request.requestId !== 'string' || request.requestId.length === 0) {
|
|
1019
|
+
throw new Error('approval response needs requestId')
|
|
1020
|
+
}
|
|
1021
|
+
if (!['allow-version', 'allow-future', 'reject'].includes(request.decision)) {
|
|
1022
|
+
throw new Error('approval response has an invalid decision')
|
|
1023
|
+
}
|
|
1024
|
+
const pending = pendingApprovals.get(request.requestId)
|
|
1025
|
+
if (pending === undefined) return
|
|
1026
|
+
pendingApprovals.delete(request.requestId)
|
|
1027
|
+
publishApprovals()
|
|
1028
|
+
if (request.decision === 'reject') {
|
|
1029
|
+
await requestAgent(CORDIS_METHODS.resolveRequestRun, {
|
|
1030
|
+
requestId: request.requestId,
|
|
1031
|
+
resolution: { ok: false, reason: 'rejected' },
|
|
1032
|
+
})
|
|
1033
|
+
return
|
|
1034
|
+
}
|
|
1035
|
+
await runClient(pending, false, request.decision === 'allow-future')
|
|
1036
|
+
}
|
|
1037
|
+
|
|
973
1038
|
async function selectTheme(params) {
|
|
974
1039
|
const request = params !== null && typeof params === 'object' ? params : {}
|
|
975
1040
|
if (request.protocol !== 0) throw new Error('unsupported theme selection payload')
|
|
@@ -1042,12 +1107,71 @@ export function attachTuiClient(opts) {
|
|
|
1042
1107
|
}
|
|
1043
1108
|
}
|
|
1044
1109
|
|
|
1110
|
+
async function selectUi(params) {
|
|
1111
|
+
const request = params !== null && typeof params === 'object' ? params : {}
|
|
1112
|
+
if (request.protocol !== 0) throw new Error('unsupported UI selection payload')
|
|
1113
|
+
if (typeof request.agentId !== 'string' || request.agentId.length === 0) {
|
|
1114
|
+
throw new Error('UI selection needs agentId')
|
|
1115
|
+
}
|
|
1116
|
+
if (typeof request.id !== 'string' || request.id.length === 0) {
|
|
1117
|
+
throw new Error('UI selection needs id')
|
|
1118
|
+
}
|
|
1119
|
+
if (!tuiPresets?.list?.().some((entry) => entry.id === request.id)) {
|
|
1120
|
+
throw new Error(`UI Plugin "${request.id}" is not registered`)
|
|
1121
|
+
}
|
|
1122
|
+
const previousId = tuiPresets.active()
|
|
1123
|
+
const previousOwner = tuiPresets.owner?.(previousId)
|
|
1124
|
+
const targetOwner = tuiPresets.owner?.(request.id)
|
|
1125
|
+
|
|
1126
|
+
if (targetOwner === undefined || tuiPresets.isLoaded?.(request.id) === true) {
|
|
1127
|
+
tuiPresets.activate(request.id)
|
|
1128
|
+
if (previousOwner !== undefined && previousOwner !== targetOwner
|
|
1129
|
+
&& tuiTheme.owner?.(tuiTheme.active()) !== previousOwner) {
|
|
1130
|
+
await stopOwner(request.agentId, previousOwner)
|
|
1131
|
+
}
|
|
1132
|
+
return { ok: true, status: 'selected', id: request.id }
|
|
1133
|
+
}
|
|
1134
|
+
if (localPlugins?.has?.(targetOwner) === true) {
|
|
1135
|
+
await localPlugins.start(targetOwner)
|
|
1136
|
+
if (tuiPresets.isLoaded?.(request.id) !== true) {
|
|
1137
|
+
throw new Error(`local UI Plugin "${targetOwner}" did not load UI "${request.id}"`)
|
|
1138
|
+
}
|
|
1139
|
+
tuiPresets.activate(request.id)
|
|
1140
|
+
if (previousOwner !== undefined && previousOwner !== targetOwner
|
|
1141
|
+
&& tuiTheme.owner?.(tuiTheme.active()) !== previousOwner) {
|
|
1142
|
+
await stopOwner(request.agentId, previousOwner)
|
|
1143
|
+
}
|
|
1144
|
+
return { ok: true, status: 'selected', id: request.id }
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
pendingUiSelections.set(targetOwner, request.id)
|
|
1148
|
+
try {
|
|
1149
|
+
const started = await requestAgent(CORDIS_METHODS.pluginStart, {
|
|
1150
|
+
agentId: request.agentId,
|
|
1151
|
+
pluginId: targetOwner,
|
|
1152
|
+
})
|
|
1153
|
+
if (started?.ok === false) {
|
|
1154
|
+
throw new Error(typeof started.message === 'string'
|
|
1155
|
+
? started.message
|
|
1156
|
+
: `failed to start UI Plugin "${targetOwner}"`)
|
|
1157
|
+
}
|
|
1158
|
+
return { ok: true, status: 'starting', id: request.id }
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
pendingUiSelections.delete(targetOwner)
|
|
1161
|
+
throw error
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1045
1165
|
function dispose() {
|
|
1046
1166
|
for (const release of loaded.values()) release()
|
|
1047
1167
|
loaded.clear()
|
|
1168
|
+
if (pendingApprovals.size > 0) {
|
|
1169
|
+
pendingApprovals.clear()
|
|
1170
|
+
publishApprovals()
|
|
1171
|
+
}
|
|
1048
1172
|
}
|
|
1049
1173
|
|
|
1050
|
-
return { onHost, selectTheme, sync, dispose }
|
|
1174
|
+
return { onHost, respondApproval, selectTheme, selectUi, sync, dispose }
|
|
1051
1175
|
}
|
|
1052
1176
|
|
|
1053
1177
|
/** Mount the TUI counterpart of the Web Cordis Client runner. */
|
|
@@ -1064,12 +1188,12 @@ export function apply(ctx) {
|
|
|
1064
1188
|
const localPlugins = ctx.get?.('tuiLocalPlugins')
|
|
1065
1189
|
let client
|
|
1066
1190
|
const service = {
|
|
1067
|
-
bindTransport(requestAgent) {
|
|
1191
|
+
bindTransport(requestAgent, notifyTui) {
|
|
1068
1192
|
client?.dispose()
|
|
1069
1193
|
client = attachTuiClient({
|
|
1070
1194
|
ctx, tuiTheme, tuiPresets, tuiSlots, tuiCommands, tuiOverlay, acpSessionConfig, acpSessionPlan,
|
|
1071
1195
|
acpSessionStats, acpSessionStatus, localPlugins,
|
|
1072
|
-
requestAgent,
|
|
1196
|
+
requestAgent, notifyTui,
|
|
1073
1197
|
})
|
|
1074
1198
|
const attached = client
|
|
1075
1199
|
return () => {
|
|
@@ -1085,6 +1209,14 @@ export function apply(ctx) {
|
|
|
1085
1209
|
if (client === undefined) throw new Error('TUI Cordis Client runner is not attached')
|
|
1086
1210
|
return client.selectTheme(params)
|
|
1087
1211
|
},
|
|
1212
|
+
selectUi(params) {
|
|
1213
|
+
if (client === undefined) throw new Error('TUI Cordis Client runner is not attached')
|
|
1214
|
+
return client.selectUi(params)
|
|
1215
|
+
},
|
|
1216
|
+
respondApproval(params) {
|
|
1217
|
+
if (client === undefined) throw new Error('TUI Cordis Client runner is not attached')
|
|
1218
|
+
return client.respondApproval(params)
|
|
1219
|
+
},
|
|
1088
1220
|
sync() {
|
|
1089
1221
|
client?.sync()
|
|
1090
1222
|
},
|
package/lib/kanagawa.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery palette pack `kanagawa`. Registers complete dark/light token maps:
|
|
3
|
+
* dark from the Kanagawa Wave variant, light from Kanagawa Lotus
|
|
4
|
+
* (terminalcolors.com/themes/kanagawa). Does not activate: `/theme` covers
|
|
5
|
+
* it. `inject = ['tuiTheme']`: sibling profile row, not `ctx.plugin` inside
|
|
6
|
+
* the runner.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from 'node:fs'
|
|
10
|
+
|
|
11
|
+
const kanagawaPalette = JSON.parse(
|
|
12
|
+
readFileSync(new URL('./palettes/kanagawa.json', import.meta.url), 'utf8'),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
export const name = 'tui-theme-kanagawa'
|
|
16
|
+
export const inject = ['tuiTheme']
|
|
17
|
+
|
|
18
|
+
export function apply(ctx) {
|
|
19
|
+
ctx.effect(() => ctx.tuiTheme.register(kanagawaPalette, { activate: false }))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { kanagawaPalette }
|
package/lib/mux.js
CHANGED
|
@@ -21,6 +21,8 @@ const COMPOSITOR_METHODS = Object.freeze(new Set([
|
|
|
21
21
|
CORDIS_METHODS.commandInvoke,
|
|
22
22
|
CORDIS_METHODS.overlayEvent,
|
|
23
23
|
CORDIS_METHODS.sessionConfigSet,
|
|
24
|
+
CORDIS_METHODS.approvalRespond,
|
|
25
|
+
CORDIS_METHODS.uiSelected,
|
|
24
26
|
]))
|
|
25
27
|
|
|
26
28
|
/** Agent → TUI Node extras. Not compositor paint; Rust never sees these. */
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "ayu",
|
|
3
|
+
"label": "Ayu",
|
|
4
|
+
"dark": {
|
|
5
|
+
"bg": "#0b0e14",
|
|
6
|
+
"surface": "#0b0e14",
|
|
7
|
+
"panel": "#1e232b",
|
|
8
|
+
"fg": "#bfbdb6",
|
|
9
|
+
"fg_secondary": "#686868",
|
|
10
|
+
"fg_tertiary": "#686868",
|
|
11
|
+
"caption": "#686868",
|
|
12
|
+
"brand": "#53bdfa",
|
|
13
|
+
"brand_soft": "#cda1fa",
|
|
14
|
+
"bubble_bg": "#1b3a5b",
|
|
15
|
+
"bubble_fg": "#bfbdb6",
|
|
16
|
+
"border": "#686868",
|
|
17
|
+
"code_bg": "#0b0e14",
|
|
18
|
+
"ok": "#7fd962",
|
|
19
|
+
"warn": "#f9af4f",
|
|
20
|
+
"err": "#ea6c73",
|
|
21
|
+
"hint": "#90e1c6",
|
|
22
|
+
"chip_bg": "#1e232b"
|
|
23
|
+
},
|
|
24
|
+
"light": {
|
|
25
|
+
"bg": "#f8f9fa",
|
|
26
|
+
"surface": "#f8f9fa",
|
|
27
|
+
"panel": "#d1d1d1",
|
|
28
|
+
"fg": "#5c6166",
|
|
29
|
+
"fg_secondary": "#686868",
|
|
30
|
+
"fg_tertiary": "#686868",
|
|
31
|
+
"caption": "#686868",
|
|
32
|
+
"brand": "#3199e1",
|
|
33
|
+
"brand_soft": "#9e75c7",
|
|
34
|
+
"bubble_bg": "#d3e1f5",
|
|
35
|
+
"bubble_fg": "#5c6166",
|
|
36
|
+
"border": "#686868",
|
|
37
|
+
"code_bg": "#f8f9fa",
|
|
38
|
+
"ok": "#6cbf43",
|
|
39
|
+
"warn": "#eca944",
|
|
40
|
+
"err": "#ea6c6d",
|
|
41
|
+
"hint": "#46ba94",
|
|
42
|
+
"chip_bg": "#d1d1d1"
|
|
43
|
+
}
|
|
44
|
+
}
|