@stina/extension-api 0.7.1 → 0.8.1
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/dist/{chunk-SS4D3JYX.js → chunk-IKJ37ZGB.js} +1 -1
- package/dist/chunk-IKJ37ZGB.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -6
- package/dist/index.d.ts +33 -6
- package/dist/index.js +1 -1
- package/dist/runtime.cjs +116 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +117 -1
- package/dist/runtime.js.map +1 -1
- package/dist/{types-Brv9O9NY.d.cts → types-Cr8eCJ0G.d.cts} +413 -2
- package/dist/{types-Brv9O9NY.d.ts → types-Cr8eCJ0G.d.ts} +413 -2
- package/package.json +1 -1
- package/src/index.ts +56 -0
- package/src/messages.ts +50 -1
- package/src/runtime.ts +146 -0
- package/src/types.components.ts +236 -0
- package/src/types.ts +234 -0
- package/dist/chunk-SS4D3JYX.js.map +0 -1
package/src/runtime.ts
CHANGED
|
@@ -13,11 +13,21 @@ import type {
|
|
|
13
13
|
SettingsAPI,
|
|
14
14
|
ProvidersAPI,
|
|
15
15
|
ToolsAPI,
|
|
16
|
+
ActionsAPI,
|
|
17
|
+
EventsAPI,
|
|
18
|
+
SchedulerAPI,
|
|
19
|
+
SchedulerJobRequest,
|
|
20
|
+
SchedulerFirePayload,
|
|
21
|
+
UserAPI,
|
|
22
|
+
UserProfile,
|
|
23
|
+
ChatAPI,
|
|
24
|
+
ChatInstructionMessage,
|
|
16
25
|
DatabaseAPI,
|
|
17
26
|
StorageAPI,
|
|
18
27
|
LogAPI,
|
|
19
28
|
AIProvider,
|
|
20
29
|
Tool,
|
|
30
|
+
Action,
|
|
21
31
|
ChatMessage,
|
|
22
32
|
ChatOptions,
|
|
23
33
|
GetModelsOptions,
|
|
@@ -81,7 +91,9 @@ let extensionContext: ExtensionContext | null = null
|
|
|
81
91
|
const pendingRequests = new Map<string, PendingRequest>()
|
|
82
92
|
const registeredProviders = new Map<string, AIProvider>()
|
|
83
93
|
const registeredTools = new Map<string, Tool>()
|
|
94
|
+
const registeredActions = new Map<string, Action>()
|
|
84
95
|
const settingsCallbacks: Array<(key: string, value: unknown) => void> = []
|
|
96
|
+
const schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []
|
|
85
97
|
|
|
86
98
|
const REQUEST_TIMEOUT = 30000 // 30 seconds
|
|
87
99
|
|
|
@@ -136,6 +148,10 @@ async function handleHostMessage(message: HostToWorkerMessage): Promise<void> {
|
|
|
136
148
|
handleSettingsChanged(message.payload.key, message.payload.value)
|
|
137
149
|
break
|
|
138
150
|
|
|
151
|
+
case 'scheduler-fire':
|
|
152
|
+
handleSchedulerFire(message.payload)
|
|
153
|
+
break
|
|
154
|
+
|
|
139
155
|
case 'provider-chat-request':
|
|
140
156
|
await handleProviderChatRequest(message.id, message.payload)
|
|
141
157
|
break
|
|
@@ -148,6 +164,10 @@ async function handleHostMessage(message: HostToWorkerMessage): Promise<void> {
|
|
|
148
164
|
await handleToolExecuteRequest(message.id, message.payload)
|
|
149
165
|
break
|
|
150
166
|
|
|
167
|
+
case 'action-execute-request':
|
|
168
|
+
await handleActionExecuteRequest(message.id, message.payload)
|
|
169
|
+
break
|
|
170
|
+
|
|
151
171
|
case 'response':
|
|
152
172
|
handleResponse(message.payload)
|
|
153
173
|
break
|
|
@@ -218,7 +238,9 @@ async function handleDeactivate(): Promise<void> {
|
|
|
218
238
|
extensionContext = null
|
|
219
239
|
registeredProviders.clear()
|
|
220
240
|
registeredTools.clear()
|
|
241
|
+
registeredActions.clear()
|
|
221
242
|
settingsCallbacks.length = 0
|
|
243
|
+
schedulerCallbacks.length = 0
|
|
222
244
|
}
|
|
223
245
|
}
|
|
224
246
|
|
|
@@ -232,6 +254,16 @@ function handleSettingsChanged(key: string, value: unknown): void {
|
|
|
232
254
|
}
|
|
233
255
|
}
|
|
234
256
|
|
|
257
|
+
function handleSchedulerFire(payload: SchedulerFirePayload): void {
|
|
258
|
+
for (const callback of schedulerCallbacks) {
|
|
259
|
+
try {
|
|
260
|
+
callback(payload)
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('Error in scheduler callback:', error)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
235
267
|
// ============================================================================
|
|
236
268
|
// Provider / Tool Requests
|
|
237
269
|
// ============================================================================
|
|
@@ -373,6 +405,45 @@ async function handleToolExecuteRequest(
|
|
|
373
405
|
}
|
|
374
406
|
}
|
|
375
407
|
|
|
408
|
+
async function handleActionExecuteRequest(
|
|
409
|
+
requestId: string,
|
|
410
|
+
payload: { actionId: string; params: Record<string, unknown> }
|
|
411
|
+
): Promise<void> {
|
|
412
|
+
const action = registeredActions.get(payload.actionId)
|
|
413
|
+
if (!action) {
|
|
414
|
+
postMessage({
|
|
415
|
+
type: 'action-execute-response',
|
|
416
|
+
payload: {
|
|
417
|
+
requestId,
|
|
418
|
+
result: { success: false, error: `Action ${payload.actionId} not found` },
|
|
419
|
+
error: `Action ${payload.actionId} not found`,
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const result = await action.execute(payload.params)
|
|
427
|
+
postMessage({
|
|
428
|
+
type: 'action-execute-response',
|
|
429
|
+
payload: {
|
|
430
|
+
requestId,
|
|
431
|
+
result,
|
|
432
|
+
},
|
|
433
|
+
})
|
|
434
|
+
} catch (error) {
|
|
435
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
436
|
+
postMessage({
|
|
437
|
+
type: 'action-execute-response',
|
|
438
|
+
payload: {
|
|
439
|
+
requestId,
|
|
440
|
+
result: { success: false, error: errorMessage },
|
|
441
|
+
error: errorMessage,
|
|
442
|
+
},
|
|
443
|
+
})
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
376
447
|
// ============================================================================
|
|
377
448
|
// Context Building
|
|
378
449
|
// ============================================================================
|
|
@@ -490,6 +561,79 @@ function buildContext(
|
|
|
490
561
|
;(context as { tools: ToolsAPI }).tools = toolsApi
|
|
491
562
|
}
|
|
492
563
|
|
|
564
|
+
// Add actions API if permitted
|
|
565
|
+
if (hasPermission('actions.register')) {
|
|
566
|
+
const actionsApi: ActionsAPI = {
|
|
567
|
+
register(action: Action): Disposable {
|
|
568
|
+
registeredActions.set(action.id, action)
|
|
569
|
+
postMessage({
|
|
570
|
+
type: 'action-registered',
|
|
571
|
+
payload: {
|
|
572
|
+
id: action.id,
|
|
573
|
+
},
|
|
574
|
+
})
|
|
575
|
+
return {
|
|
576
|
+
dispose: () => {
|
|
577
|
+
registeredActions.delete(action.id)
|
|
578
|
+
},
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
}
|
|
582
|
+
;(context as { actions: ActionsAPI }).actions = actionsApi
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Add events API if permitted
|
|
586
|
+
if (hasPermission('events.emit')) {
|
|
587
|
+
const eventsApi: EventsAPI = {
|
|
588
|
+
async emit(name: string, payload?: Record<string, unknown>): Promise<void> {
|
|
589
|
+
await sendRequest<void>('events.emit', { name, payload })
|
|
590
|
+
},
|
|
591
|
+
}
|
|
592
|
+
;(context as { events: EventsAPI }).events = eventsApi
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Add scheduler API if permitted
|
|
596
|
+
if (hasPermission('scheduler.register')) {
|
|
597
|
+
const schedulerApi: SchedulerAPI = {
|
|
598
|
+
async schedule(job: SchedulerJobRequest): Promise<void> {
|
|
599
|
+
await sendRequest<void>('scheduler.schedule', { job })
|
|
600
|
+
},
|
|
601
|
+
async cancel(jobId: string): Promise<void> {
|
|
602
|
+
await sendRequest<void>('scheduler.cancel', { jobId })
|
|
603
|
+
},
|
|
604
|
+
onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {
|
|
605
|
+
schedulerCallbacks.push(callback)
|
|
606
|
+
return {
|
|
607
|
+
dispose: () => {
|
|
608
|
+
const index = schedulerCallbacks.indexOf(callback)
|
|
609
|
+
if (index >= 0) schedulerCallbacks.splice(index, 1)
|
|
610
|
+
},
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
}
|
|
614
|
+
;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Add user profile API if permitted
|
|
618
|
+
if (hasPermission('user.profile.read')) {
|
|
619
|
+
const userApi: UserAPI = {
|
|
620
|
+
async getProfile(): Promise<UserProfile> {
|
|
621
|
+
return sendRequest<UserProfile>('user.getProfile', {})
|
|
622
|
+
},
|
|
623
|
+
}
|
|
624
|
+
;(context as { user: UserAPI }).user = userApi
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Add chat API if permitted
|
|
628
|
+
if (hasPermission('chat.message.write')) {
|
|
629
|
+
const chatApi: ChatAPI = {
|
|
630
|
+
async appendInstruction(message: ChatInstructionMessage): Promise<void> {
|
|
631
|
+
await sendRequest<void>('chat.appendInstruction', message)
|
|
632
|
+
},
|
|
633
|
+
}
|
|
634
|
+
;(context as { chat: ChatAPI }).chat = chatApi
|
|
635
|
+
}
|
|
636
|
+
|
|
493
637
|
// Add database API if permitted
|
|
494
638
|
if (hasPermission('database.own')) {
|
|
495
639
|
const databaseApi: DatabaseAPI = {
|
|
@@ -556,6 +700,8 @@ export type {
|
|
|
556
700
|
ToolDefinition,
|
|
557
701
|
ToolResult,
|
|
558
702
|
ToolCall,
|
|
703
|
+
Action,
|
|
704
|
+
ActionResult,
|
|
559
705
|
ModelInfo,
|
|
560
706
|
ChatMessage,
|
|
561
707
|
ChatOptions,
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/** Base interface for dynamically rendered extension components. */
|
|
2
|
+
export interface ExtensionComponentData {
|
|
3
|
+
component: string
|
|
4
|
+
[key: string]: unknown
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// =============================================================================
|
|
8
|
+
// Iteration & Children
|
|
9
|
+
// =============================================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Iterator for rendering a list of components from data.
|
|
13
|
+
* Used with layout components like VerticalStack, HorizontalStack, Grid.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```json
|
|
17
|
+
* {
|
|
18
|
+
* "each": "$todos",
|
|
19
|
+
* "as": "todo",
|
|
20
|
+
* "items": [{ "component": "Label", "text": "$todo.title" }]
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface ExtensionComponentIterator {
|
|
25
|
+
/** Data source to iterate over. Use "$name" for dynamic reference or inline array. */
|
|
26
|
+
each: string | unknown[]
|
|
27
|
+
/** Variable name for current item in scope */
|
|
28
|
+
as: string
|
|
29
|
+
/** Components to render for each item */
|
|
30
|
+
items: ExtensionComponentData[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Children can be either a static array of components or an iterator.
|
|
35
|
+
*/
|
|
36
|
+
export type ExtensionComponentChildren =
|
|
37
|
+
| ExtensionComponentData[]
|
|
38
|
+
| ExtensionComponentIterator
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Actions
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Action call with parameters.
|
|
46
|
+
* Used for component events like onClick, onChange, etc.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```json
|
|
50
|
+
* {
|
|
51
|
+
* "action": "deleteTodo",
|
|
52
|
+
* "params": { "todoId": "$todo.id" }
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export interface ExtensionActionCall {
|
|
57
|
+
/** Name of the registered action */
|
|
58
|
+
action: string
|
|
59
|
+
/** Parameters to pass. Values starting with "$" are resolved from scope. */
|
|
60
|
+
params?: Record<string, unknown>
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Action reference - can be a simple string (action name) or full action call.
|
|
65
|
+
*/
|
|
66
|
+
export type ExtensionActionRef = string | ExtensionActionCall
|
|
67
|
+
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Data Sources & Panel Definition
|
|
70
|
+
// =============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Data source definition for fetching data via an action.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```json
|
|
77
|
+
* {
|
|
78
|
+
* "action": "getProjects",
|
|
79
|
+
* "params": { "includeArchived": false },
|
|
80
|
+
* "refreshOn": ["project.changed"]
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export interface ExtensionDataSource {
|
|
85
|
+
/** Action to call for fetching data */
|
|
86
|
+
action: string
|
|
87
|
+
/** Parameters to pass to the action */
|
|
88
|
+
params?: Record<string, unknown>
|
|
89
|
+
/** Event names that should trigger a refresh of this data */
|
|
90
|
+
refreshOn?: string[]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Panel definition for extension-contributed panels.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```json
|
|
98
|
+
* {
|
|
99
|
+
* "data": {
|
|
100
|
+
* "projects": { "action": "getProjectsWithTodos", "refreshOn": ["todo.changed"] }
|
|
101
|
+
* },
|
|
102
|
+
* "content": {
|
|
103
|
+
* "component": "VerticalStack",
|
|
104
|
+
* "children": { "each": "$projects", "as": "project", "items": [...] }
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export interface ExtensionPanelDefinition {
|
|
110
|
+
/** Data sources available in the panel. Keys become variable names (e.g., "$projects"). */
|
|
111
|
+
data?: Record<string, ExtensionDataSource>
|
|
112
|
+
/** Root component to render */
|
|
113
|
+
content: ExtensionComponentData
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The extension API properties for the Header component. */
|
|
117
|
+
export interface HeaderProps extends ExtensionComponentData {
|
|
118
|
+
component: 'Header'
|
|
119
|
+
level: number
|
|
120
|
+
title: string
|
|
121
|
+
description?: string | string[]
|
|
122
|
+
icon?: string
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The extension API properties for the Label component. */
|
|
126
|
+
export interface LabelProps extends ExtensionComponentData {
|
|
127
|
+
component: 'Label'
|
|
128
|
+
text: string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The extension API properties for the paragraph component. */
|
|
132
|
+
export interface ParagraphProps extends ExtensionComponentData {
|
|
133
|
+
component: 'Paragraph'
|
|
134
|
+
text: string
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The extension API properties for the Button component. */
|
|
138
|
+
export interface ButtonProps extends ExtensionComponentData {
|
|
139
|
+
component: 'Button'
|
|
140
|
+
text: string
|
|
141
|
+
onClickAction: ExtensionActionRef
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The extension API properties for the TextInput component. */
|
|
145
|
+
export interface TextInputProps extends ExtensionComponentData {
|
|
146
|
+
component: 'TextInput'
|
|
147
|
+
label: string
|
|
148
|
+
placeholder?: string
|
|
149
|
+
value?: string
|
|
150
|
+
onChangeAction: ExtensionActionRef
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The extension API properties for the Select component. */
|
|
154
|
+
export interface SelectProps extends ExtensionComponentData {
|
|
155
|
+
component: 'Select'
|
|
156
|
+
label: string
|
|
157
|
+
options: Array<{ label: string; value: string }>
|
|
158
|
+
selectedValue?: string
|
|
159
|
+
onChangeAction: ExtensionActionRef
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** The extension API properties for the VerticalStack component. */
|
|
163
|
+
export interface VerticalStackProps extends ExtensionComponentData {
|
|
164
|
+
component: 'VerticalStack'
|
|
165
|
+
gap?: number
|
|
166
|
+
children: ExtensionComponentChildren
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The extension API properties for the HorizontalStack component. */
|
|
170
|
+
export interface HorizontalStackProps extends ExtensionComponentData {
|
|
171
|
+
component: 'HorizontalStack'
|
|
172
|
+
gap?: number
|
|
173
|
+
children: ExtensionComponentChildren
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The extension API properties for the Grid component. */
|
|
177
|
+
export interface GridProps extends ExtensionComponentData {
|
|
178
|
+
component: 'Grid'
|
|
179
|
+
columns: number
|
|
180
|
+
gap?: number
|
|
181
|
+
children: ExtensionComponentChildren
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The extension API properties for the Divider component. */
|
|
185
|
+
export interface DividerProps extends ExtensionComponentData {
|
|
186
|
+
component: 'Divider'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The extension API properties for the Icon component. */
|
|
190
|
+
export interface IconProps extends ExtensionComponentData {
|
|
191
|
+
component: 'Icon'
|
|
192
|
+
name: string
|
|
193
|
+
title?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Button type for IconButton. */
|
|
197
|
+
export type IconButtonType = 'normal' | 'primary' | 'danger' | 'accent'
|
|
198
|
+
|
|
199
|
+
/** The extension API properties for the IconButton component. */
|
|
200
|
+
export interface IconButtonProps extends ExtensionComponentData {
|
|
201
|
+
component: 'IconButton'
|
|
202
|
+
icon: string
|
|
203
|
+
tooltip: string
|
|
204
|
+
active?: boolean
|
|
205
|
+
disabled?: boolean
|
|
206
|
+
type?: IconButtonType
|
|
207
|
+
onClickAction: ExtensionActionRef
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Action button definition for Panel component. */
|
|
211
|
+
export interface PanelAction {
|
|
212
|
+
icon: string
|
|
213
|
+
tooltip: string
|
|
214
|
+
action: ExtensionActionRef
|
|
215
|
+
type?: IconButtonType
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The extension API properties for the Panel component. */
|
|
219
|
+
export interface PanelProps extends ExtensionComponentData {
|
|
220
|
+
component: 'Panel'
|
|
221
|
+
title: string
|
|
222
|
+
description?: string | string[]
|
|
223
|
+
icon?: string
|
|
224
|
+
actions?: PanelAction[]
|
|
225
|
+
content?: ExtensionComponentData
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** The extension API properties for the Toggle component. */
|
|
229
|
+
export interface ToggleProps extends ExtensionComponentData {
|
|
230
|
+
component: 'Toggle'
|
|
231
|
+
label?: string
|
|
232
|
+
description?: string
|
|
233
|
+
checked?: boolean
|
|
234
|
+
disabled?: boolean
|
|
235
|
+
onChangeAction: ExtensionActionRef
|
|
236
|
+
}
|