@makinbakin/sdk 0.0.0-bootstrap.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/_internal/app/components/agent-avatar.d.ts +11 -0
- package/_internal/app/components/agent-filter.d.ts +8 -0
- package/_internal/app/components/agent-select.d.ts +15 -0
- package/_internal/app/components/agent-status.d.ts +8 -0
- package/_internal/app/components/bakin-drawer.d.ts +23 -0
- package/_internal/app/components/color-picker.d.ts +7 -0
- package/_internal/app/components/empty-state.d.ts +10 -0
- package/_internal/app/components/error-banner.d.ts +6 -0
- package/_internal/app/components/error-state.d.ts +8 -0
- package/_internal/app/components/facet-filter.d.ts +17 -0
- package/_internal/app/components/integrated-brainstorm/collapsed-header.d.ts +14 -0
- package/_internal/app/components/integrated-brainstorm/empty-state.d.ts +5 -0
- package/_internal/app/components/integrated-brainstorm/index.d.ts +3 -0
- package/_internal/app/components/integrated-brainstorm/input-row.d.ts +16 -0
- package/_internal/app/components/integrated-brainstorm/message-list.d.ts +8 -0
- package/_internal/app/components/integrated-brainstorm/thinking-indicator.d.ts +19 -0
- package/_internal/app/components/integrated-brainstorm/types.d.ts +61 -0
- package/_internal/app/components/integrated-brainstorm/use-auto-grow.d.ts +6 -0
- package/_internal/app/components/integrated-brainstorm/use-brainstorm-state.d.ts +21 -0
- package/_internal/app/components/markdown-content.d.ts +3 -0
- package/_internal/app/components/markdown-editor.d.ts +11 -0
- package/_internal/app/components/model-select.d.ts +9 -0
- package/_internal/app/components/page-layout.d.ts +9 -0
- package/_internal/app/components/plugin-header.d.ts +16 -0
- package/_internal/app/components/plugin-settings-renderer.d.ts +9 -0
- package/_internal/app/components/sortable-head.d.ts +13 -0
- package/_internal/app/components/ui/alert.d.ts +10 -0
- package/_internal/app/components/ui/avatar.d.ts +11 -0
- package/_internal/app/components/ui/badge.d.ts +7 -0
- package/_internal/app/components/ui/button.d.ts +8 -0
- package/_internal/app/components/ui/card.d.ts +11 -0
- package/_internal/app/components/ui/checkbox.d.ts +3 -0
- package/_internal/app/components/ui/collapsible.d.ts +5 -0
- package/_internal/app/components/ui/command.d.ts +19 -0
- package/_internal/app/components/ui/dialog.d.ts +17 -0
- package/_internal/app/components/ui/dropdown-menu.d.ts +29 -0
- package/_internal/app/components/ui/form.d.ts +23 -0
- package/_internal/app/components/ui/input-group.d.ts +18 -0
- package/_internal/app/components/ui/input.d.ts +3 -0
- package/_internal/app/components/ui/label.d.ts +3 -0
- package/_internal/app/components/ui/popover.d.ts +9 -0
- package/_internal/app/components/ui/progress.d.ts +7 -0
- package/_internal/app/components/ui/select.d.ts +15 -0
- package/_internal/app/components/ui/separator.d.ts +3 -0
- package/_internal/app/components/ui/sheet.d.ts +14 -0
- package/_internal/app/components/ui/skeleton.d.ts +2 -0
- package/_internal/app/components/ui/switch.d.ts +5 -0
- package/_internal/app/components/ui/table.d.ts +10 -0
- package/_internal/app/components/ui/tabs.d.ts +10 -0
- package/_internal/app/components/ui/textarea.d.ts +3 -0
- package/_internal/app/components/ui/tooltip.d.ts +6 -0
- package/_internal/app/components/underline-tabs.d.ts +15 -0
- package/_internal/app/hooks/use-assets.d.ts +25 -0
- package/_internal/app/hooks/use-content-store.d.ts +36 -0
- package/_internal/app/hooks/use-debug.d.ts +1 -0
- package/_internal/app/hooks/use-form-guard.d.ts +5 -0
- package/_internal/app/hooks/use-query-state.d.ts +14 -0
- package/_internal/app/hooks/use-runtime-status.d.ts +12 -0
- package/_internal/app/hooks/use-schedule.d.ts +63 -0
- package/_internal/app/hooks/use-search.d.ts +65 -0
- package/_internal/app/hooks/use-sidebar.d.ts +4 -0
- package/_internal/app/hooks/use-sse.d.ts +1 -0
- package/_internal/app/hooks/use-toast.d.ts +16 -0
- package/_internal/app/hooks/use-vertical-resize.d.ts +22 -0
- package/_internal/app/lib/browser-notify.d.ts +5 -0
- package/_internal/app/lib/map-audit-message.d.ts +7 -0
- package/_internal/app/lib/utils.d.ts +2 -0
- package/_internal/app/types/index.d.ts +126 -0
- package/_internal/core/adapters/runtime/capabilities.d.ts +2 -0
- package/_internal/core/adapters/runtime/concepts.d.ts +458 -0
- package/_internal/core/adapters/runtime/helpers.d.ts +5 -0
- package/_internal/core/adapters/runtime/index.d.ts +5 -0
- package/_internal/core/adapters/shared.d.ts +37 -0
- package/_internal/core/docs/index.d.ts +2 -0
- package/_internal/core/docs/metadata.d.ts +92 -0
- package/_internal/core/docs/route.d.ts +9 -0
- package/_internal/core/format.d.ts +6 -0
- package/_internal/core/openapi/errors.d.ts +29 -0
- package/_internal/core/openapi/zod-to-openapi.d.ts +14 -0
- package/_internal/core/plugin-types.d.ts +849 -0
- package/_internal/core/routing/define.d.ts +34 -0
- package/_internal/core/routing/dispatcher.d.ts +44 -0
- package/_internal/core/routing/index.d.ts +15 -0
- package/_internal/core/routing/operation-id.d.ts +9 -0
- package/_internal/core/routing/registry.d.ts +52 -0
- package/_internal/core/routing/search-route.d.ts +26 -0
- package/_internal/core/routing/types.d.ts +123 -0
- package/_internal/plugins/team/hooks/use-agent-store.d.ts +49 -0
- package/_internal/plugins/team/types.d.ts +131 -0
- package/_internal/plugins/workflows/hooks/channel-icon.d.ts +6 -0
- package/_internal/plugins/workflows/hooks/use-notification-channels.d.ts +13 -0
- package/_internal/plugins/workflows/lib/notification-channel-registry.d.ts +26 -0
- package/components/index.d.ts +33 -0
- package/components/index.js +39522 -0
- package/hooks/index.d.ts +25 -0
- package/hooks/index.js +1738 -0
- package/hooks/router.d.ts +38 -0
- package/index.d.ts +27 -0
- package/index.js +13917 -0
- package/metadata/index.d.ts +16 -0
- package/metadata/index.js +31 -0
- package/package.json +88 -0
- package/register.d.ts +103 -0
- package/routing/index.d.ts +11 -0
- package/routing/index.js +13721 -0
- package/slots/index.d.ts +55 -0
- package/slots/index.js +92 -0
- package/types/index.d.ts +840 -0
- package/types/index.js +1 -0
- package/ui/index.d.ts +34 -0
- package/ui/index.js +28713 -0
- package/utils/index.d.ts +9 -0
- package/utils/index.js +1832 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin system type definitions for Bakin.
|
|
3
|
+
* All plugin interfaces are defined here — no behavioral changes.
|
|
4
|
+
*/
|
|
5
|
+
import type { ZodRawShape, ZodType } from 'zod';
|
|
6
|
+
import type { ContractStability, ContractVisibility, DocsExample, SchemaLike, SourceLocation } from './docs';
|
|
7
|
+
import type { AgentRuntimeAdapter } from './adapters/runtime';
|
|
8
|
+
import type { APIRoute as DeclarativeAPIRoute, PluginContextLite } from './routing/types';
|
|
9
|
+
export interface ApprovalActor {
|
|
10
|
+
id: string;
|
|
11
|
+
displayName?: string;
|
|
12
|
+
source: 'channel' | 'web' | 'system';
|
|
13
|
+
}
|
|
14
|
+
export interface StorageAdapter {
|
|
15
|
+
read(path: string): string | null;
|
|
16
|
+
write(path: string, content: string): void;
|
|
17
|
+
append(path: string, content: string): void;
|
|
18
|
+
exists(path: string): boolean;
|
|
19
|
+
readAll(): Record<string, string>;
|
|
20
|
+
list?(path?: string): string[];
|
|
21
|
+
remove?(path: string): void;
|
|
22
|
+
rename?(from: string, to: string): void;
|
|
23
|
+
stat?(path: string): {
|
|
24
|
+
path: string;
|
|
25
|
+
size: number;
|
|
26
|
+
mtimeMs: number;
|
|
27
|
+
isFile: boolean;
|
|
28
|
+
isDirectory: boolean;
|
|
29
|
+
} | null;
|
|
30
|
+
readJson?<T = unknown>(path: string): T | null;
|
|
31
|
+
writeJson?(path: string, value: unknown): void;
|
|
32
|
+
/**
|
|
33
|
+
* Convert a plugin-storage-relative path or glob to the content-dir-relative
|
|
34
|
+
* path seen by file-backed search/watch APIs. Implementations never return
|
|
35
|
+
* absolute host paths.
|
|
36
|
+
*/
|
|
37
|
+
searchPath?(path: string): string;
|
|
38
|
+
}
|
|
39
|
+
export interface EventBus {
|
|
40
|
+
emit(event: string, data?: Record<string, unknown>): void;
|
|
41
|
+
on(pattern: string, handler: (event: string, data: Record<string, unknown>) => void): () => void;
|
|
42
|
+
once(pattern: string, handler: (event: string, data: Record<string, unknown>) => void): () => void;
|
|
43
|
+
}
|
|
44
|
+
export interface NavItem {
|
|
45
|
+
id: string;
|
|
46
|
+
label: string;
|
|
47
|
+
icon: string;
|
|
48
|
+
href: string;
|
|
49
|
+
order?: number;
|
|
50
|
+
children?: NavItem[];
|
|
51
|
+
alwaysExpanded?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface APIRoute {
|
|
54
|
+
path: string;
|
|
55
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
56
|
+
handler: (req: Request, ctx: PluginContext) => Response | Promise<Response>;
|
|
57
|
+
summary?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
params?: string;
|
|
60
|
+
input?: SchemaLike;
|
|
61
|
+
output?: SchemaLike;
|
|
62
|
+
visibility?: ContractVisibility;
|
|
63
|
+
stability?: ContractStability;
|
|
64
|
+
examples?: DocsExample[];
|
|
65
|
+
source?: SourceLocation;
|
|
66
|
+
permissions?: string[];
|
|
67
|
+
}
|
|
68
|
+
export interface UISlotRegistration {
|
|
69
|
+
slot: string;
|
|
70
|
+
component: React.ComponentType<Record<string, unknown>>;
|
|
71
|
+
order?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface ContentFile {
|
|
74
|
+
path: string;
|
|
75
|
+
}
|
|
76
|
+
/** Result returned by execution tool handlers */
|
|
77
|
+
export interface ExecToolResult {
|
|
78
|
+
ok: boolean;
|
|
79
|
+
error?: string;
|
|
80
|
+
details?: unknown;
|
|
81
|
+
[key: string]: unknown;
|
|
82
|
+
}
|
|
83
|
+
/** Definition for a registerable execution tool */
|
|
84
|
+
/** Context available to exec tool handlers — provides access to plugin services */
|
|
85
|
+
export interface PluginToolContext {
|
|
86
|
+
storage: StorageAdapter;
|
|
87
|
+
events: EventBus;
|
|
88
|
+
pluginId: string;
|
|
89
|
+
runtime: AgentRuntimeAdapter;
|
|
90
|
+
tasks: PluginTaskService;
|
|
91
|
+
assets: AssetsAPI;
|
|
92
|
+
search: SearchAPI;
|
|
93
|
+
hooks: HookAPI;
|
|
94
|
+
activity: ActivityAPI;
|
|
95
|
+
getSettings<T = Record<string, unknown>>(): T;
|
|
96
|
+
}
|
|
97
|
+
export interface ExecToolDefinition {
|
|
98
|
+
name: string;
|
|
99
|
+
description: string;
|
|
100
|
+
label?: string;
|
|
101
|
+
activityDuplicate?: boolean;
|
|
102
|
+
parameters: ZodRawShape;
|
|
103
|
+
handler: (params: Record<string, unknown>, agent: string, ctx?: PluginToolContext) => Promise<ExecToolResult>;
|
|
104
|
+
source?: string;
|
|
105
|
+
}
|
|
106
|
+
/** A skill that can be registered by plugins or loaded from disk */
|
|
107
|
+
export interface SkillDefinition {
|
|
108
|
+
name: string;
|
|
109
|
+
instructions: string;
|
|
110
|
+
output_schema?: Record<string, unknown>;
|
|
111
|
+
source?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface WorkflowDefinitionInput {
|
|
114
|
+
/** Stable workflow id. Falls back to slug(name) when omitted. */
|
|
115
|
+
id?: string;
|
|
116
|
+
name: string;
|
|
117
|
+
description: string;
|
|
118
|
+
version: number;
|
|
119
|
+
inputs?: Record<string, unknown>;
|
|
120
|
+
steps: unknown[];
|
|
121
|
+
}
|
|
122
|
+
export interface ActivityAPI {
|
|
123
|
+
/** Log a human-readable message to the live activity feed */
|
|
124
|
+
log(agent: string, message: string, opts?: {
|
|
125
|
+
taskId?: string;
|
|
126
|
+
category?: string;
|
|
127
|
+
}): void;
|
|
128
|
+
/** Log a structured audit event */
|
|
129
|
+
audit(event: string, agent: string, data?: Record<string, unknown>): void;
|
|
130
|
+
}
|
|
131
|
+
export interface HookAPI {
|
|
132
|
+
/** Register a handler for a named hook. Returns unsubscribe function. */
|
|
133
|
+
register(name: string, handler: (data: any) => any, metadata?: HookRegistrationMetadata): () => void;
|
|
134
|
+
/** Run registered handlers as a waterfall and return the final value. */
|
|
135
|
+
call<T>(name: string, data: T): Promise<T>;
|
|
136
|
+
/** Run every registered handler and ignore return values. */
|
|
137
|
+
callAll(name: string, data: Record<string, unknown>): Promise<void>;
|
|
138
|
+
/** Check if any handlers are registered for a hook. */
|
|
139
|
+
has(name: string): boolean;
|
|
140
|
+
/** Invoke a hook and return its result (RPC-style). */
|
|
141
|
+
invoke<R>(name: string, data: unknown): Promise<R | undefined>;
|
|
142
|
+
}
|
|
143
|
+
export interface HookRegistrationMetadata {
|
|
144
|
+
label?: string;
|
|
145
|
+
summary: string;
|
|
146
|
+
description?: string;
|
|
147
|
+
hookKind?: ContractHookKind;
|
|
148
|
+
input?: SchemaLike;
|
|
149
|
+
output?: SchemaLike;
|
|
150
|
+
visibility?: ContractVisibility;
|
|
151
|
+
stability?: ContractStability;
|
|
152
|
+
examples?: DocsExample[];
|
|
153
|
+
}
|
|
154
|
+
export type ContractHookKind = 'rpc' | 'event' | 'waterfall';
|
|
155
|
+
/** Field types the workflow canvas knows how to render. */
|
|
156
|
+
export type FormFieldType = 'string' | 'text' | 'number' | 'boolean' | 'select' | 'agent' | 'skill' | 'list';
|
|
157
|
+
/** One input shown in the node's config drawer. */
|
|
158
|
+
export interface FormField {
|
|
159
|
+
name: string;
|
|
160
|
+
type: FormFieldType;
|
|
161
|
+
required?: boolean;
|
|
162
|
+
description?: string;
|
|
163
|
+
options?: {
|
|
164
|
+
value: string;
|
|
165
|
+
label: string;
|
|
166
|
+
}[];
|
|
167
|
+
}
|
|
168
|
+
/** Edge connection rules enforced by the canvas editor's onConnect validator. */
|
|
169
|
+
export interface EdgeRules {
|
|
170
|
+
/** Max inbound edges allowed; undefined = unlimited. */
|
|
171
|
+
maxInbound?: number;
|
|
172
|
+
/** Max outbound edges allowed; undefined = unlimited. 0 = terminal (no outgoing). */
|
|
173
|
+
maxOutbound?: number;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Input shape plugins pass to `ctx.registerNodeType`. The plugin id is
|
|
177
|
+
* prepended to `kind` automatically (`{pluginId}.{kind}`), so two plugins
|
|
178
|
+
* can ship the same unprefixed kind without colliding.
|
|
179
|
+
*
|
|
180
|
+
* The Zod schema validates the full step object (including `id`, `type`,
|
|
181
|
+
* `label`, plus any plugin-specific fields). It is checked against step
|
|
182
|
+
* definitions loaded from YAML and against step bodies built by the canvas
|
|
183
|
+
* editor — one definition, no drift.
|
|
184
|
+
*/
|
|
185
|
+
export interface PluginNodeTypeInput<T = unknown> {
|
|
186
|
+
kind: string;
|
|
187
|
+
zodSchema: ZodType<T>;
|
|
188
|
+
formFields: FormField[];
|
|
189
|
+
/** Defaults to `{ maxOutbound: 1 }` (agent-style) when omitted. */
|
|
190
|
+
edgeRules?: EdgeRules;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Input shape plugins pass to `ctx.registerNotificationChannel`. The plugin id
|
|
194
|
+
* is prepended to `id` automatically (`{pluginId}.{id}`), matching the node-
|
|
195
|
+
* type precedent. Built-in workflows-plugin runtime channels (general,
|
|
196
|
+
* announcements, alerts, email) self-register at module load in
|
|
197
|
+
* `plugins/workflows/lib/notification-channel-registry.ts`.
|
|
198
|
+
*/
|
|
199
|
+
export interface PluginNotificationChannelInput {
|
|
200
|
+
id: string;
|
|
201
|
+
label: string;
|
|
202
|
+
/** Optional 2-character badge (e.g. "DC", "IG"). Falls back to `id.slice(0, 2).toUpperCase()` at render time. */
|
|
203
|
+
initials?: string;
|
|
204
|
+
/** Lucide-react icon export name (e.g. "MessageSquare"). Unknown names fall back to HelpCircle at render time. */
|
|
205
|
+
icon?: string;
|
|
206
|
+
}
|
|
207
|
+
export interface NotificationChannelDef extends PluginNotificationChannelInput {
|
|
208
|
+
runtime: 'builtin' | 'plugin';
|
|
209
|
+
/** Set when runtime === 'plugin'; identifies the owning plugin. */
|
|
210
|
+
pluginId?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Canonical result shape for a single doctor check row.
|
|
214
|
+
*/
|
|
215
|
+
export interface HealthCheckResult {
|
|
216
|
+
check: string;
|
|
217
|
+
status: 'ok' | 'warn' | 'error' | 'fixed';
|
|
218
|
+
message: string;
|
|
219
|
+
autoFixable: boolean;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Input shape plugins pass to `ctx.registerHealthCheck`. The plugin id is
|
|
223
|
+
* auto-namespaced as `{pluginId}.{id}`. `run()` returns an array so one
|
|
224
|
+
* registered check can contribute multiple result rows (mirrors how the
|
|
225
|
+
* existing `checkPersonas` returns one per agent).
|
|
226
|
+
*/
|
|
227
|
+
export interface PluginHealthCheckInput {
|
|
228
|
+
id: string;
|
|
229
|
+
name: string;
|
|
230
|
+
/**
|
|
231
|
+
* Runs the check, returns any number of result rows. Throws/rejects are
|
|
232
|
+
* caught by the doctor orchestrator and converted to a synthetic error
|
|
233
|
+
* result — a single bad handler never crashes the sweep.
|
|
234
|
+
*/
|
|
235
|
+
run: () => Promise<HealthCheckResult[]>;
|
|
236
|
+
/**
|
|
237
|
+
* Advisory flag for admin UIs: `true` if `run()` may perform safe
|
|
238
|
+
* auto-fixes internally. Pure metadata in v1 — the orchestrator always
|
|
239
|
+
* invokes every registered check regardless of this value. Plugins that
|
|
240
|
+
* do internal auto-fixes gate on `getSettings().doctor.*` themselves.
|
|
241
|
+
*/
|
|
242
|
+
autoFix?: boolean;
|
|
243
|
+
}
|
|
244
|
+
export interface HealthCheckDef extends PluginHealthCheckInput {
|
|
245
|
+
runtime: 'plugin';
|
|
246
|
+
pluginId: string;
|
|
247
|
+
}
|
|
248
|
+
export interface PluginContext {
|
|
249
|
+
storage: StorageAdapter;
|
|
250
|
+
events: EventBus;
|
|
251
|
+
pluginId: string;
|
|
252
|
+
runtime: AgentRuntimeAdapter;
|
|
253
|
+
tasks: PluginTaskService;
|
|
254
|
+
assets: AssetsAPI;
|
|
255
|
+
registerNav(items: NavItem[]): void;
|
|
256
|
+
/**
|
|
257
|
+
* @deprecated Use `definePlugin({ routes: [defineRoute({...})] })` to declare
|
|
258
|
+
* routes. This adapter remains during the migration window for any
|
|
259
|
+
* out-of-tree plugin that still calls `ctx.registerRoute(...)` from
|
|
260
|
+
* `activate()`. In-repo plugins migrated in T6–T13 / T20 — none of them
|
|
261
|
+
* call this. The dispatcher adapts the legacy shape (input → body,
|
|
262
|
+
* output → responses[200]) when invoked through this path.
|
|
263
|
+
*/
|
|
264
|
+
registerRoute(route: APIRoute): void;
|
|
265
|
+
registerSlot(registration: UISlotRegistration): void;
|
|
266
|
+
registerExecTool(tool: ExecToolDefinition): void;
|
|
267
|
+
registerSkill(skill: SkillDefinition): void;
|
|
268
|
+
/**
|
|
269
|
+
* Register a workflow definition shipped by this plugin. Disk-resident
|
|
270
|
+
* user definitions in `~/.bakin/workflows/definitions/` always win on id
|
|
271
|
+
* collision (user-wins). A second plugin claiming an id already taken by
|
|
272
|
+
* another plugin is a containment error: it's logged but does not throw
|
|
273
|
+
* out of `activate()`. Re-registering the same id from the same plugin
|
|
274
|
+
* is idempotent (newer wins) so hot reload works.
|
|
275
|
+
*/
|
|
276
|
+
registerWorkflow(definition: WorkflowDefinitionInput, opts?: {
|
|
277
|
+
readOnly?: boolean;
|
|
278
|
+
}): void;
|
|
279
|
+
/**
|
|
280
|
+
* Register a workflow node type owned by this plugin. The kind is
|
|
281
|
+
* auto-namespaced to `{pluginId}.{kind}` so plugins can't stomp each
|
|
282
|
+
* other. Returns the namespaced kind the plugin should use to register
|
|
283
|
+
* a matching `workflows.executeNode.{namespacedKind}` hook handler and
|
|
284
|
+
* to reference the kind from its node renderer export.
|
|
285
|
+
*/
|
|
286
|
+
registerNodeType<T = unknown>(def: PluginNodeTypeInput<T>): string;
|
|
287
|
+
/**
|
|
288
|
+
* Register a notification channel owned by this plugin. The id is
|
|
289
|
+
* auto-namespaced to `{pluginId}.{id}`. Returns the namespaced id so
|
|
290
|
+
* callers can reference it from downstream code and tests. Built-in
|
|
291
|
+
* workflows-plugin channels register directly (without the plugin prefix)
|
|
292
|
+
* via the registry module's lazy self-seed.
|
|
293
|
+
*/
|
|
294
|
+
registerNotificationChannel(def: PluginNotificationChannelInput): string;
|
|
295
|
+
/**
|
|
296
|
+
* Register a health check owned by this plugin. The id is auto-namespaced
|
|
297
|
+
* to `{pluginId}.{id}`. `run()` is invoked during every `runDiagnostics()`
|
|
298
|
+
* sweep — throws are isolated, a single bad check never crashes the
|
|
299
|
+
* doctor. Returns the namespaced id.
|
|
300
|
+
*/
|
|
301
|
+
registerHealthCheck(def: PluginHealthCheckInput): string;
|
|
302
|
+
watchFiles(patterns: string[]): void;
|
|
303
|
+
/** Read this plugin's persisted settings */
|
|
304
|
+
getSettings<T = Record<string, unknown>>(): T;
|
|
305
|
+
/** Merge a partial update into this plugin's settings and persist */
|
|
306
|
+
updateSettings(patch: Record<string, unknown>): void;
|
|
307
|
+
/** Structured activity logging */
|
|
308
|
+
activity: ActivityAPI;
|
|
309
|
+
/** Cross-plugin hook registration */
|
|
310
|
+
hooks: HookAPI;
|
|
311
|
+
/** Adapter-backed search — register content types, index, query */
|
|
312
|
+
search: SearchAPI;
|
|
313
|
+
}
|
|
314
|
+
export type PluginTaskColumn = 'backlog' | 'todo' | 'inProgress' | 'review' | 'done' | 'blocked' | 'archived';
|
|
315
|
+
export interface PluginTask {
|
|
316
|
+
id: string;
|
|
317
|
+
title: string;
|
|
318
|
+
agent?: string;
|
|
319
|
+
createdBy?: string;
|
|
320
|
+
checked: boolean;
|
|
321
|
+
column: PluginTaskColumn;
|
|
322
|
+
date?: string;
|
|
323
|
+
blockedReason?: string;
|
|
324
|
+
description?: string;
|
|
325
|
+
log?: TaskLogEntry[];
|
|
326
|
+
dependsOn?: string;
|
|
327
|
+
parentId?: string | null;
|
|
328
|
+
workflowId?: string;
|
|
329
|
+
scheduleJobId?: string;
|
|
330
|
+
projectId?: string;
|
|
331
|
+
order?: number;
|
|
332
|
+
createdAt?: string;
|
|
333
|
+
updatedAt?: string;
|
|
334
|
+
}
|
|
335
|
+
export interface TaskLogEntry {
|
|
336
|
+
timestamp: string;
|
|
337
|
+
author: string;
|
|
338
|
+
message: string;
|
|
339
|
+
data?: Record<string, unknown>;
|
|
340
|
+
}
|
|
341
|
+
export interface PluginTaskCreateInput {
|
|
342
|
+
id?: string;
|
|
343
|
+
title: string;
|
|
344
|
+
description?: string;
|
|
345
|
+
agent?: string;
|
|
346
|
+
createdBy?: string;
|
|
347
|
+
column?: PluginTaskColumn;
|
|
348
|
+
date?: string;
|
|
349
|
+
workflowId?: string;
|
|
350
|
+
projectId?: string;
|
|
351
|
+
parentId?: string | null;
|
|
352
|
+
skipWorkflowReason?: string;
|
|
353
|
+
}
|
|
354
|
+
export interface PluginTaskUpdateInput {
|
|
355
|
+
title?: string;
|
|
356
|
+
description?: string;
|
|
357
|
+
agent?: string;
|
|
358
|
+
createdBy?: string;
|
|
359
|
+
checked?: boolean;
|
|
360
|
+
column?: PluginTaskColumn;
|
|
361
|
+
date?: string;
|
|
362
|
+
blockedReason?: string;
|
|
363
|
+
workflowId?: string;
|
|
364
|
+
scheduleJobId?: string;
|
|
365
|
+
projectId?: string;
|
|
366
|
+
parentId?: string | null;
|
|
367
|
+
}
|
|
368
|
+
export interface PluginTaskService {
|
|
369
|
+
create(input: PluginTaskCreateInput): Promise<PluginTask>;
|
|
370
|
+
update(id: string, patch: PluginTaskUpdateInput): Promise<PluginTask>;
|
|
371
|
+
move(id: string, column: PluginTaskColumn, order?: number): Promise<PluginTask>;
|
|
372
|
+
remove(id: string): Promise<void>;
|
|
373
|
+
get(id: string): Promise<PluginTask | null>;
|
|
374
|
+
list(filter?: {
|
|
375
|
+
column?: PluginTaskColumn;
|
|
376
|
+
agent?: string;
|
|
377
|
+
projectId?: string;
|
|
378
|
+
}): Promise<PluginTask[]>;
|
|
379
|
+
appendLog(id: string, entry: TaskLogEntry): Promise<void>;
|
|
380
|
+
}
|
|
381
|
+
export interface AssetVariantMeta {
|
|
382
|
+
role: 'thumbnail' | 'optimized' | 'webp';
|
|
383
|
+
path: string;
|
|
384
|
+
filename: string;
|
|
385
|
+
size: number;
|
|
386
|
+
mimeType: string;
|
|
387
|
+
}
|
|
388
|
+
export interface AssetMeta {
|
|
389
|
+
path: string;
|
|
390
|
+
filename: string;
|
|
391
|
+
type: 'text' | 'images' | 'video' | 'audio' | 'plans' | 'research' | 'pdf' | 'data' | 'other';
|
|
392
|
+
mimeType: string;
|
|
393
|
+
size: number;
|
|
394
|
+
mtimeMs?: number;
|
|
395
|
+
metadata: {
|
|
396
|
+
agent: string;
|
|
397
|
+
taskId: string | null;
|
|
398
|
+
created: string;
|
|
399
|
+
tool?: string;
|
|
400
|
+
description?: string;
|
|
401
|
+
tags?: string[];
|
|
402
|
+
originalFilename?: string;
|
|
403
|
+
};
|
|
404
|
+
variants?: AssetVariantMeta[];
|
|
405
|
+
}
|
|
406
|
+
export interface AssetFileRef {
|
|
407
|
+
kind: 'asset';
|
|
408
|
+
filename: string;
|
|
409
|
+
mimeType?: string;
|
|
410
|
+
}
|
|
411
|
+
export interface AssetsAPI {
|
|
412
|
+
getByFilename(filename: string): Promise<AssetMeta | null>;
|
|
413
|
+
list(filter?: {
|
|
414
|
+
type?: AssetMeta['type'];
|
|
415
|
+
taskId?: string | null;
|
|
416
|
+
}): Promise<AssetMeta[]>;
|
|
417
|
+
exists(filename: string): Promise<boolean>;
|
|
418
|
+
fileRef(filename: string): Promise<AssetFileRef>;
|
|
419
|
+
}
|
|
420
|
+
/** Field type for search content type schemas */
|
|
421
|
+
export interface SearchSchemaField {
|
|
422
|
+
type: 'text' | 'keyword' | 'number' | 'boolean' | 'datetime' | 'array';
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* One vector index on a search table. A content type can declare multiple
|
|
426
|
+
* indexes to embed the same document into several vector spaces — e.g. a
|
|
427
|
+
* text index using BGE and a visual index using CLIP on the assets table.
|
|
428
|
+
* Each index has its own embedder (resolved via embedderRef), input
|
|
429
|
+
* declaration, and optional chunker config.
|
|
430
|
+
*/
|
|
431
|
+
export interface SearchIndexDefinition {
|
|
432
|
+
/** Index name as stored by the search adapter. Must be stable across restarts. */
|
|
433
|
+
name: string;
|
|
434
|
+
/** Ref into settings.search.settings.embedders — 'default', 'visual', or custom. */
|
|
435
|
+
embedderRef: string;
|
|
436
|
+
/** Handlebars template for this index's text embedding input. */
|
|
437
|
+
embeddingTemplate?: string;
|
|
438
|
+
/**
|
|
439
|
+
* Document field containing a URL to media bytes for visual/multimodal
|
|
440
|
+
* embedding input. The search adapter owns provider-specific helper syntax.
|
|
441
|
+
*/
|
|
442
|
+
mediaUrlField?: string;
|
|
443
|
+
/** Per-index chunker config, overrides any table-level default. */
|
|
444
|
+
chunker?: {
|
|
445
|
+
enabled: boolean;
|
|
446
|
+
targetTokens?: number;
|
|
447
|
+
overlapTokens?: number;
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/** Definition for a searchable content type registered by a plugin */
|
|
451
|
+
export interface SearchContentTypeDefinition {
|
|
452
|
+
/** Table name — auto-prefixed with `bakin_`. E.g., 'tasks' → 'bakin_tasks' */
|
|
453
|
+
table: string;
|
|
454
|
+
/** Schema for the document fields */
|
|
455
|
+
schema: Record<string, SearchSchemaField>;
|
|
456
|
+
/** Fields to include in full-text search */
|
|
457
|
+
searchableFields: string[];
|
|
458
|
+
/**
|
|
459
|
+
* Handlebars template for embedding generation. Used when `indexes` is
|
|
460
|
+
* not provided — the registry synthesizes a single default index named
|
|
461
|
+
* `embeddings` with this template and the default embedder.
|
|
462
|
+
*/
|
|
463
|
+
embeddingTemplate: string;
|
|
464
|
+
/**
|
|
465
|
+
* Optional per-index definitions. When provided, overrides
|
|
466
|
+
* `embeddingTemplate` and creates one embedding index per entry with
|
|
467
|
+
* its own embedder. Used by content types that want multimodal indexing
|
|
468
|
+
* (e.g. assets with both a text index and a visual index).
|
|
469
|
+
*/
|
|
470
|
+
indexes?: SearchIndexDefinition[];
|
|
471
|
+
/** Fields to expose as aggregatable facets */
|
|
472
|
+
facets?: string[];
|
|
473
|
+
/**
|
|
474
|
+
* Document field to use as input for the cross-encoder reranker. When
|
|
475
|
+
* set, queries against this content type attach the configured reranker
|
|
476
|
+
* and score the query-document pair using the value at this field.
|
|
477
|
+
* When unset, queries skip reranking for this content type.
|
|
478
|
+
*/
|
|
479
|
+
rerankField?: string;
|
|
480
|
+
/** TTL duration (Go format: '24h', '7d', '30d') */
|
|
481
|
+
ttl?: string;
|
|
482
|
+
/** TTL field (defaults to 'created_at') */
|
|
483
|
+
ttlField?: string;
|
|
484
|
+
/** Chunking config for long documents — used by the synthesized default index. */
|
|
485
|
+
chunker?: {
|
|
486
|
+
enabled: boolean;
|
|
487
|
+
targetTokens?: number;
|
|
488
|
+
overlapTokens?: number;
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* Backfill function — called during full/per-table reindex.
|
|
492
|
+
* Must yield ALL documents for this content type from source.
|
|
493
|
+
*/
|
|
494
|
+
reindex: () => AsyncGenerator<{
|
|
495
|
+
key: string;
|
|
496
|
+
doc: Record<string, unknown>;
|
|
497
|
+
}>;
|
|
498
|
+
/**
|
|
499
|
+
* Existence check — called during orphan cleanup.
|
|
500
|
+
* Returns true if the source document for this key still exists.
|
|
501
|
+
*/
|
|
502
|
+
verifyExists: (key: string) => Promise<boolean>;
|
|
503
|
+
}
|
|
504
|
+
/** Parameters for a search query */
|
|
505
|
+
export interface SearchQueryParams {
|
|
506
|
+
/** Search query string */
|
|
507
|
+
q: string;
|
|
508
|
+
/** Structured keyword filters */
|
|
509
|
+
filters?: Record<string, string | boolean | number>;
|
|
510
|
+
/** Facets to include in aggregations */
|
|
511
|
+
facets?: string[];
|
|
512
|
+
/** Max results */
|
|
513
|
+
limit?: number;
|
|
514
|
+
/** Result offset for pagination */
|
|
515
|
+
offset?: number;
|
|
516
|
+
/**
|
|
517
|
+
* Whether to run the cross-encoder reranker on results. Defaults to
|
|
518
|
+
* true when the reranker is enabled in settings. Set false for latency-
|
|
519
|
+
* sensitive paths (facet-only queries, ID lookups, bulk scans) where
|
|
520
|
+
* the extra ~100-500ms isn't worth it.
|
|
521
|
+
*/
|
|
522
|
+
rerank?: boolean;
|
|
523
|
+
/**
|
|
524
|
+
* Raw adapter-specific aggregations passed through unchanged to the query layer.
|
|
525
|
+
* Use for date histograms, range buckets, stats aggregations, or any
|
|
526
|
+
* other shape beyond the term-facet convenience in `facets`. Merged
|
|
527
|
+
* with facet-derived aggregations (these win on key collision).
|
|
528
|
+
* See the active search adapter docs for the aggregation schema.
|
|
529
|
+
*/
|
|
530
|
+
aggregations?: Record<string, unknown>;
|
|
531
|
+
/**
|
|
532
|
+
* Search strategy override. Defaults to the site-wide search strategy.
|
|
533
|
+
* Pass 'full_text_only' for filter-driven
|
|
534
|
+
* counts or ID lookups — semantic search rejects `limit: 0` queries
|
|
535
|
+
* with the "semantic search requires topk limit to be positive" error.
|
|
536
|
+
*/
|
|
537
|
+
strategy?: 'rrf' | 'semantic_only' | 'full_text_only';
|
|
538
|
+
}
|
|
539
|
+
/** A single search result */
|
|
540
|
+
export interface SearchResult {
|
|
541
|
+
id: string;
|
|
542
|
+
table: string;
|
|
543
|
+
score: number;
|
|
544
|
+
fields: Record<string, unknown>;
|
|
545
|
+
/** Cross-encoder reranker score (present when a reranker was used). */
|
|
546
|
+
rerankScore?: number;
|
|
547
|
+
}
|
|
548
|
+
/** Search response from a query */
|
|
549
|
+
export interface SearchResponse {
|
|
550
|
+
results: SearchResult[];
|
|
551
|
+
/**
|
|
552
|
+
* Mapped term-facet aggregations — keyed by facet field, each a list
|
|
553
|
+
* of { value, count }. Populated from `params.facets` for convenience.
|
|
554
|
+
*/
|
|
555
|
+
aggregations?: Record<string, Array<{
|
|
556
|
+
value: string;
|
|
557
|
+
count: number;
|
|
558
|
+
}>>;
|
|
559
|
+
/**
|
|
560
|
+
* Raw aggregation response from the search adapter, unmodified. Populated whenever
|
|
561
|
+
* the underlying query returned aggregations — use this for non-term
|
|
562
|
+
* shapes like date_histogram, range, stats, etc.
|
|
563
|
+
*/
|
|
564
|
+
rawAggregations?: Record<string, unknown>;
|
|
565
|
+
meta: {
|
|
566
|
+
query: string;
|
|
567
|
+
total: number;
|
|
568
|
+
took_ms: number;
|
|
569
|
+
source: 'search' | 'fallback';
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
export interface SearchHealthIndex {
|
|
573
|
+
name: string;
|
|
574
|
+
type: string;
|
|
575
|
+
totalIndexed: number;
|
|
576
|
+
walBacklog: number;
|
|
577
|
+
error?: string;
|
|
578
|
+
rebuilding: boolean;
|
|
579
|
+
backfillProgress?: number;
|
|
580
|
+
}
|
|
581
|
+
export interface SearchHealthTable {
|
|
582
|
+
table: string;
|
|
583
|
+
pluginId: string;
|
|
584
|
+
stats: Record<string, unknown> | null;
|
|
585
|
+
indexHealth?: SearchHealthIndex[];
|
|
586
|
+
healthy: boolean;
|
|
587
|
+
}
|
|
588
|
+
export interface SearchHealthSnapshot {
|
|
589
|
+
enabled: boolean;
|
|
590
|
+
tables: SearchHealthTable[];
|
|
591
|
+
}
|
|
592
|
+
/** Atomic transform operation (update fields without re-embedding) */
|
|
593
|
+
export interface SearchTransformOp {
|
|
594
|
+
op: '$set' | '$inc' | '$push';
|
|
595
|
+
field?: string;
|
|
596
|
+
value: unknown;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* One file-pattern → mapper pair used by `FileBackedContentTypeDefinition`.
|
|
600
|
+
* A registration may carry multiple of these when a single content type
|
|
601
|
+
* draws from several distinct file shapes (e.g. workflows store
|
|
602
|
+
* definitions as `.yaml` and instances as `.json`, both indexed into the
|
|
603
|
+
* same `bakin_workflows` table with different key prefixes).
|
|
604
|
+
*/
|
|
605
|
+
export interface FilePatternMapper {
|
|
606
|
+
/** Glob pattern relative to contentDir. First-match-wins across the patterns array. */
|
|
607
|
+
pattern: string;
|
|
608
|
+
/**
|
|
609
|
+
* Derive the search key for a file matching this pattern. Return null
|
|
610
|
+
* to skip the file (e.g. it matches the glob but is missing required
|
|
611
|
+
* companion data).
|
|
612
|
+
*/
|
|
613
|
+
fileToId: (relPath: string) => string | null;
|
|
614
|
+
/**
|
|
615
|
+
* Build the search document for a file matching this pattern.
|
|
616
|
+
* `content` is the raw file bytes as a UTF-8 string (empty for binary
|
|
617
|
+
* assets, which the watcher recognizes by their `assets/` prefix).
|
|
618
|
+
* Return null to skip indexing (e.g. derived state isn't ready yet).
|
|
619
|
+
*/
|
|
620
|
+
fileToDoc: (relPath: string, content: string) => Promise<Record<string, unknown> | null>;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* A content type whose source of truth is one or more files under
|
|
624
|
+
* `~/.bakin/`. Registering via `registerFileBackedContentType` wires up:
|
|
625
|
+
*
|
|
626
|
+
* 1. Standard `registerContentType` (table creation, schema, reindex).
|
|
627
|
+
* 2. A watcher sync hook that re-indexes on add/change.
|
|
628
|
+
* 3. A watcher unlink hook that removes on delete.
|
|
629
|
+
* 4. A startup reconcile that detects mtime drift and corrects it.
|
|
630
|
+
*
|
|
631
|
+
* Plugins should reach for this helper first. Use raw `registerContentType`
|
|
632
|
+
* only when the source of truth is NOT the filesystem (e.g. SQLite,
|
|
633
|
+
* external API, runtime adapter).
|
|
634
|
+
*/
|
|
635
|
+
export interface FileBackedContentTypeDefinition extends SearchContentTypeDefinition {
|
|
636
|
+
/**
|
|
637
|
+
* One or more file-pattern mappers. Patterns should not overlap;
|
|
638
|
+
* first match wins. A file that matches no pattern is ignored.
|
|
639
|
+
*/
|
|
640
|
+
filePatterns: FilePatternMapper[];
|
|
641
|
+
/**
|
|
642
|
+
* Optional exclude patterns applied BEFORE any mapper matches. Useful
|
|
643
|
+
* for skipping subdirectories like `assets/**\/.trash/**` or sidecar
|
|
644
|
+
* files like `**\/*.meta.json`.
|
|
645
|
+
*/
|
|
646
|
+
excludePatterns?: string[];
|
|
647
|
+
/**
|
|
648
|
+
* Escape hatch for sync events. When provided, the helper invokes
|
|
649
|
+
* this instead of the default `fileToDoc → ctx.search.index` flow for
|
|
650
|
+
* any file matching this content type's patterns. The plugin takes
|
|
651
|
+
* full responsibility for indexing.
|
|
652
|
+
*
|
|
653
|
+
* Use when pairing logic (e.g. assets' binary↔sidecar coupling) can't
|
|
654
|
+
* be expressed as a single mapper. The matched `FilePatternMapper` is
|
|
655
|
+
* still consulted for `fileToId` if the plugin needs the canonical key.
|
|
656
|
+
*/
|
|
657
|
+
onSync?: (relPath: string, content: string) => Promise<void>;
|
|
658
|
+
/**
|
|
659
|
+
* Escape hatch for unlink events. Same story as `onSync`. If unset,
|
|
660
|
+
* the helper calls the matched mapper's `fileToId(relPath)` and then
|
|
661
|
+
* `ctx.search.remove(id)`.
|
|
662
|
+
*/
|
|
663
|
+
onUnlink?: (relPath: string) => Promise<void>;
|
|
664
|
+
/**
|
|
665
|
+
* Whether to run the startup reconcile loop on plugin activation.
|
|
666
|
+
* Defaults to true. Set false only when the plugin wants to drive its
|
|
667
|
+
* own initial population (rare).
|
|
668
|
+
*/
|
|
669
|
+
buildOnStartup?: boolean;
|
|
670
|
+
}
|
|
671
|
+
/** Search API provided to plugins via ctx.search */
|
|
672
|
+
export interface SearchAPI {
|
|
673
|
+
/**
|
|
674
|
+
* Register a content type this plugin will index.
|
|
675
|
+
* Must be called during activate(). Creates the search table if needed.
|
|
676
|
+
*
|
|
677
|
+
* Prefer `registerFileBackedContentType` when the source of truth is
|
|
678
|
+
* a file under `~/.bakin/` — it wires up watcher hooks and startup
|
|
679
|
+
* reconcile automatically. Use this raw form only for non-filesystem
|
|
680
|
+
* sources (SQLite, external APIs, runtime adapters).
|
|
681
|
+
*/
|
|
682
|
+
registerContentType(def: SearchContentTypeDefinition): void;
|
|
683
|
+
/**
|
|
684
|
+
* Register a file-backed content type. Wraps `registerContentType` and
|
|
685
|
+
* additionally wires watcher sync + unlink hooks scoped to the declared
|
|
686
|
+
* file patterns, plus a startup reconcile that detects mtime drift on
|
|
687
|
+
* boot. This is the blessed API for any plugin whose data lives in the
|
|
688
|
+
* filesystem under `~/.bakin/`.
|
|
689
|
+
*/
|
|
690
|
+
registerFileBackedContentType(def: FileBackedContentTypeDefinition): void;
|
|
691
|
+
/** Index or update a document. Fire-and-forget. */
|
|
692
|
+
index(key: string, doc: Record<string, unknown>): Promise<void>;
|
|
693
|
+
/** Remove a document from the index. */
|
|
694
|
+
remove(key: string): Promise<void>;
|
|
695
|
+
/** Atomic field update without re-embedding. For metadata-only changes. */
|
|
696
|
+
transform(key: string, operations: SearchTransformOp[]): Promise<void>;
|
|
697
|
+
/** Search this plugin's content type. */
|
|
698
|
+
query(params: SearchQueryParams): Promise<SearchResponse>;
|
|
699
|
+
/** Global search adapter and registered-table health snapshot. */
|
|
700
|
+
health?(): Promise<SearchHealthSnapshot>;
|
|
701
|
+
/**
|
|
702
|
+
* Plugin-scoped maintenance operations for indexers that own non-file-backed
|
|
703
|
+
* tables. This is intentionally narrower than the raw adapter: callers can
|
|
704
|
+
* only touch their own registered content type.
|
|
705
|
+
*/
|
|
706
|
+
maintenance?: SearchMaintenanceAPI;
|
|
707
|
+
}
|
|
708
|
+
export interface SearchMaintenanceAPI {
|
|
709
|
+
/** Whether the backing search service is reachable. */
|
|
710
|
+
available(): Promise<boolean>;
|
|
711
|
+
/** Iterate every document in this plugin's registered content type. */
|
|
712
|
+
scan(): AsyncIterable<{
|
|
713
|
+
key: string;
|
|
714
|
+
document: Record<string, unknown>;
|
|
715
|
+
}>;
|
|
716
|
+
/** Remove several documents from this plugin's registered content type. */
|
|
717
|
+
batchRemove(keys: string[]): Promise<number>;
|
|
718
|
+
/** Drop and recreate this plugin's registered content type table. */
|
|
719
|
+
resetContentType(): Promise<void>;
|
|
720
|
+
}
|
|
721
|
+
interface BaseSettingsField {
|
|
722
|
+
key: string;
|
|
723
|
+
label: string;
|
|
724
|
+
description?: string;
|
|
725
|
+
/** If true, the renderer blocks save when the field is empty (checked inside list rows). */
|
|
726
|
+
required?: boolean;
|
|
727
|
+
}
|
|
728
|
+
export interface StringSettingsField extends BaseSettingsField {
|
|
729
|
+
type: 'string';
|
|
730
|
+
default?: string;
|
|
731
|
+
}
|
|
732
|
+
export interface NumberSettingsField extends BaseSettingsField {
|
|
733
|
+
type: 'number';
|
|
734
|
+
default?: number;
|
|
735
|
+
}
|
|
736
|
+
export interface BooleanSettingsField extends BaseSettingsField {
|
|
737
|
+
type: 'boolean';
|
|
738
|
+
default?: boolean;
|
|
739
|
+
}
|
|
740
|
+
export interface SelectSettingsField extends BaseSettingsField {
|
|
741
|
+
type: 'select';
|
|
742
|
+
options: {
|
|
743
|
+
value: string;
|
|
744
|
+
label: string;
|
|
745
|
+
}[];
|
|
746
|
+
default?: string;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* List-of-rows field. Each row renders the fields declared in `itemShape`.
|
|
750
|
+
* The persisted value is `Array<Record<string, unknown>>`. Reusable by any
|
|
751
|
+
* plugin that needs a user-editable taxonomy (messaging content types,
|
|
752
|
+
* future notification channels, etc).
|
|
753
|
+
*/
|
|
754
|
+
export interface ListSettingsField extends BaseSettingsField {
|
|
755
|
+
type: 'list';
|
|
756
|
+
/** Keyed map of sub-fields rendered per row. Nested lists are not supported. */
|
|
757
|
+
itemShape: Record<string, StringSettingsField | NumberSettingsField | BooleanSettingsField | SelectSettingsField>;
|
|
758
|
+
default?: unknown[];
|
|
759
|
+
addLabel?: string;
|
|
760
|
+
minItems?: number;
|
|
761
|
+
maxItems?: number;
|
|
762
|
+
/**
|
|
763
|
+
* If set, the renderer blocks save when two rows share the same value for
|
|
764
|
+
* this sub-field key. Typical use: uniqueField: 'id' on a taxonomy list.
|
|
765
|
+
*/
|
|
766
|
+
uniqueField?: string;
|
|
767
|
+
}
|
|
768
|
+
export type SettingsField = StringSettingsField | NumberSettingsField | BooleanSettingsField | SelectSettingsField | ListSettingsField;
|
|
769
|
+
export interface PluginSettingsSchema {
|
|
770
|
+
fields: SettingsField[];
|
|
771
|
+
}
|
|
772
|
+
export interface BakinPlugin {
|
|
773
|
+
id: string;
|
|
774
|
+
name: string;
|
|
775
|
+
version: string;
|
|
776
|
+
activate(ctx: PluginContext): void | Promise<void>;
|
|
777
|
+
/** Called after ALL plugins have been activated */
|
|
778
|
+
onReady?(): void | Promise<void>;
|
|
779
|
+
/** Called during graceful shutdown (reverse activation order) */
|
|
780
|
+
onShutdown?(): void | Promise<void>;
|
|
781
|
+
/** Called when this plugin's settings are updated */
|
|
782
|
+
onSettingsChange?(settings: Record<string, unknown>): void | Promise<void>;
|
|
783
|
+
/**
|
|
784
|
+
* Called by `bakin plugins remove` BEFORE Bakin tears down the plugin's
|
|
785
|
+
* own bookkeeping (registry rows, settings JSON, plugin dir, etc.).
|
|
786
|
+
* Plugin's responsibility: clean up any data it wrote OUTSIDE its own
|
|
787
|
+
* dir (custom files in user dirs, rows in shared tables, etc.). Bakin
|
|
788
|
+
* handles plugin-owned bookkeeping itself.
|
|
789
|
+
*
|
|
790
|
+
* Receives the same full PluginContext that activate() received — no
|
|
791
|
+
* reduced surface. Errors are logged + audited but do NOT block the
|
|
792
|
+
* rest of the cleanup; a buggy onUninstall must not trap the user in
|
|
793
|
+
* a half-removed state.
|
|
794
|
+
*/
|
|
795
|
+
onUninstall?(ctx: PluginContext): void | Promise<void>;
|
|
796
|
+
/** Declarative settings schema for auto-generated settings UI */
|
|
797
|
+
settingsSchema?: PluginSettingsSchema;
|
|
798
|
+
navItems?: NavItem[];
|
|
799
|
+
contentFiles?: ContentFile[];
|
|
800
|
+
/**
|
|
801
|
+
* Declarative HTTP routes. Registered into the route table BEFORE
|
|
802
|
+
* `activate()` runs, so handlers must use `ctx` for services rather than
|
|
803
|
+
* closing over module-scope state initialized in `activate()`.
|
|
804
|
+
*
|
|
805
|
+
* Use `defineRoute()` from `@bakin/core/routing` to author entries —
|
|
806
|
+
* a bare `routes: APIRoute[]` annotation widens types and breaks the
|
|
807
|
+
* per-route inference that drives the dispatcher's typed `parsed` argument.
|
|
808
|
+
*
|
|
809
|
+
* Optional during the migration window (T1–T16); plugins still using
|
|
810
|
+
* `ctx.registerRoute(...)` from `activate()` continue to work via the
|
|
811
|
+
* dispatcher adapter.
|
|
812
|
+
*/
|
|
813
|
+
routes?: ReadonlyArray<DeclarativeAPIRoute<PluginContextLite, any, any, any>>;
|
|
814
|
+
}
|
|
815
|
+
export interface PluginManifestSignature {
|
|
816
|
+
algorithm: 'ed25519';
|
|
817
|
+
signer: string;
|
|
818
|
+
publicKey: string;
|
|
819
|
+
signature: string;
|
|
820
|
+
}
|
|
821
|
+
export interface PluginManifest {
|
|
822
|
+
id: string;
|
|
823
|
+
name: string;
|
|
824
|
+
version: string;
|
|
825
|
+
bakin: string;
|
|
826
|
+
description: string;
|
|
827
|
+
entry: {
|
|
828
|
+
server: string;
|
|
829
|
+
client?: string;
|
|
830
|
+
};
|
|
831
|
+
contentFiles?: string[];
|
|
832
|
+
secrets?: string[];
|
|
833
|
+
tests?: string;
|
|
834
|
+
dependencies?: string[];
|
|
835
|
+
permissions?: string[];
|
|
836
|
+
signature?: PluginManifestSignature;
|
|
837
|
+
}
|
|
838
|
+
export interface PluginEntry {
|
|
839
|
+
path: string;
|
|
840
|
+
enabled?: boolean;
|
|
841
|
+
}
|
|
842
|
+
export interface BakinConfig {
|
|
843
|
+
plugins: PluginEntry[];
|
|
844
|
+
theme?: Record<string, string>;
|
|
845
|
+
storage?: {
|
|
846
|
+
contentDir?: string;
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
export {};
|