@ai_mind/ade-plugin-sdk 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +1248 -2
- package/cli/build.mjs +154 -0
- package/index.d.ts +77 -0
- package/index.mjs +9 -0
- package/package.json +21 -4
- package/types/adapter.d.ts +153 -0
- package/types/ade-types.d.ts +712 -0
- package/types/api.d.ts +207 -0
- package/types/plugins.d.ts +85 -0
package/cli/build.mjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { build } from 'esbuild'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
const modulePath = await fs.realpath(fileURLToPath(import.meta.url))
|
|
8
|
+
const sdkPackage = JSON.parse(await fs.readFile(path.resolve(path.dirname(modulePath), '../package.json'), 'utf8'))
|
|
9
|
+
const sdkVersion = /^(\d+)\.\d+\.\d+(?:[-+][\w.-]+)?$/.exec(sdkPackage.version)
|
|
10
|
+
const sdkMajor = sdkVersion ? Number(sdkVersion[1]) : NaN
|
|
11
|
+
if (!Number.isSafeInteger(sdkMajor)) throw new Error('插件 SDK 版本无效')
|
|
12
|
+
|
|
13
|
+
const sharedModules = ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom', 'react-dom/client', '@ai_mind/ade-plugin-sdk', '@ai_mind/plugin-sdk', '@ade/plugin-sdk']
|
|
14
|
+
const assetLoaders = Object.fromEntries([
|
|
15
|
+
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif', '.ico',
|
|
16
|
+
'.woff', '.woff2', '.ttf', '.otf', '.eot'
|
|
17
|
+
].map((extension) => [extension, 'file']))
|
|
18
|
+
|
|
19
|
+
function ownedPath(directory, relative, label) {
|
|
20
|
+
if (typeof relative !== 'string' || !relative || path.isAbsolute(relative)) {
|
|
21
|
+
throw new Error(`${label}必须是插件目录内的相对路径`)
|
|
22
|
+
}
|
|
23
|
+
const resolved = path.resolve(directory, relative)
|
|
24
|
+
if (!resolved.startsWith(`${directory}${path.sep}`)) throw new Error(`${label}不能越过插件目录`)
|
|
25
|
+
return resolved
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 将 TSX 和依赖预构建为浏览器插件,保留 ADE 提供的 React 模块身份。 */
|
|
29
|
+
export async function buildPlugin(directory, options = {}) {
|
|
30
|
+
directory = path.resolve(directory)
|
|
31
|
+
const manifest = JSON.parse(await fs.readFile(path.join(directory, 'plugin.json'), 'utf8'))
|
|
32
|
+
const pluginId = manifest.id ?? manifest.name
|
|
33
|
+
if (typeof pluginId !== 'string' || !/^[a-z0-9][a-z0-9._-]*$/.test(pluginId)) {
|
|
34
|
+
throw new Error('plugin.json 必须声明稳定的小写插件 ID')
|
|
35
|
+
}
|
|
36
|
+
if (manifest.apiVersion !== 1) throw new Error('插件构建工具只支持 apiVersion: 1')
|
|
37
|
+
if (manifest.rendererFormat !== 'register') {
|
|
38
|
+
throw new Error('TSX 构建要求 plugin.json 声明 rendererFormat: "register"')
|
|
39
|
+
}
|
|
40
|
+
const addedAdeMajor = !Object.hasOwn(manifest, 'adeMajor')
|
|
41
|
+
if (!addedAdeMajor && (!Number.isSafeInteger(manifest.adeMajor) || manifest.adeMajor < 0 || manifest.adeMajor !== sdkMajor)) {
|
|
42
|
+
throw new Error(`plugin.json 的 adeMajor 必须是当前 SDK 对应的 ADE major 版本 ${sdkMajor}`)
|
|
43
|
+
}
|
|
44
|
+
if (addedAdeMajor) manifest.adeMajor = sdkMajor
|
|
45
|
+
if (manifest.styles !== undefined && (!Array.isArray(manifest.styles) || manifest.styles.some((style) => typeof style !== 'string'))) {
|
|
46
|
+
throw new Error('plugin.json 的 styles 必须是相对路径数组')
|
|
47
|
+
}
|
|
48
|
+
const entry = ownedPath(directory, options.entry ?? 'src/renderer.tsx', '渲染入口')
|
|
49
|
+
const output = ownedPath(directory, manifest.renderer, 'renderer')
|
|
50
|
+
const assetDirectory = path.relative(directory, path.dirname(output)).split(path.sep).filter(Boolean).map(encodeURIComponent).join('/')
|
|
51
|
+
const result = await build({
|
|
52
|
+
absWorkingDir: directory,
|
|
53
|
+
entryPoints: [entry],
|
|
54
|
+
outfile: output,
|
|
55
|
+
bundle: true,
|
|
56
|
+
write: false,
|
|
57
|
+
platform: 'browser',
|
|
58
|
+
format: 'cjs',
|
|
59
|
+
target: 'es2022',
|
|
60
|
+
jsx: 'automatic',
|
|
61
|
+
loader: assetLoaders,
|
|
62
|
+
assetNames: 'assets/[name]-[hash]',
|
|
63
|
+
publicPath: `ade-plugin://${pluginId}/${assetDirectory ? `${assetDirectory}/` : ''}`,
|
|
64
|
+
external: sharedModules,
|
|
65
|
+
metafile: true,
|
|
66
|
+
legalComments: 'none',
|
|
67
|
+
logLevel: 'silent',
|
|
68
|
+
plugins: [{
|
|
69
|
+
name: 'ade-browser-boundary',
|
|
70
|
+
setup(builder) {
|
|
71
|
+
builder.onResolve({ filter: /^node:/ }, ({ path: name }) => ({
|
|
72
|
+
errors: [{ text: `渲染插件不能引入 ${name};请在 main 入口通过命令提供此能力` }]
|
|
73
|
+
}))
|
|
74
|
+
}
|
|
75
|
+
}]
|
|
76
|
+
})
|
|
77
|
+
const code = result.outputFiles.find((file) => file.path === output)?.text
|
|
78
|
+
if (!code) throw new Error('未生成渲染插件文件')
|
|
79
|
+
// 浏览器用 <script> 装载工厂;不使用 eval 或 new Function,也不引入第二份 React。
|
|
80
|
+
const wrapped = `globalThis.__adeRegisterPlugin(${JSON.stringify(pluginId)}, function(require, module, exports) {\n${code}\n});\n`
|
|
81
|
+
// CSS 和 url() 引用的字体、图片也是预构建产物,不能只保留入口 JS。
|
|
82
|
+
const outputFiles = result.outputFiles.map((file) => ({
|
|
83
|
+
...file,
|
|
84
|
+
relative: path.relative(directory, file.path).split(path.sep).join('/')
|
|
85
|
+
}))
|
|
86
|
+
for (const file of outputFiles) ownedPath(directory, file.relative, '构建产物')
|
|
87
|
+
await Promise.all(outputFiles.map(async (file) => {
|
|
88
|
+
await fs.mkdir(path.dirname(file.path), { recursive: true })
|
|
89
|
+
await fs.writeFile(file.path, file.path === output ? wrapped : file.contents)
|
|
90
|
+
}))
|
|
91
|
+
const generatedStyles = outputFiles.filter((file) => file.path.endsWith('.css')).map((file) => file.relative)
|
|
92
|
+
const originalStyles = manifest.styles ?? []
|
|
93
|
+
const existingStyles = new Set(originalStyles.map((style) => ownedPath(directory, style, 'styles')))
|
|
94
|
+
const addedStyles = generatedStyles.filter((style) => !existingStyles.has(path.resolve(directory, style)))
|
|
95
|
+
if (addedStyles.length) {
|
|
96
|
+
manifest.styles = [...originalStyles, ...addedStyles]
|
|
97
|
+
}
|
|
98
|
+
if (addedAdeMajor || addedStyles.length) {
|
|
99
|
+
await fs.writeFile(path.join(directory, 'plugin.json'), `${JSON.stringify(manifest, null, 2)}\n`)
|
|
100
|
+
}
|
|
101
|
+
if (options.mainEntry) {
|
|
102
|
+
await build({
|
|
103
|
+
absWorkingDir: directory,
|
|
104
|
+
entryPoints: [ownedPath(directory, options.mainEntry, '主进程入口')],
|
|
105
|
+
outfile: ownedPath(directory, manifest.main, 'main'),
|
|
106
|
+
bundle: true,
|
|
107
|
+
platform: 'node',
|
|
108
|
+
format: 'cjs',
|
|
109
|
+
target: 'node24',
|
|
110
|
+
external: ['electron'],
|
|
111
|
+
plugins: [{
|
|
112
|
+
name: 'ade-main-sdk-runtime',
|
|
113
|
+
setup(builder) {
|
|
114
|
+
builder.onResolve({ filter: /^(?:@ai_mind\/(?:ade-)?plugin-sdk|@ade\/plugin-sdk)$/ }, () => ({ path: path.resolve(path.dirname(modulePath), '../index.mjs') }))
|
|
115
|
+
}
|
|
116
|
+
}],
|
|
117
|
+
legalComments: 'none',
|
|
118
|
+
logLevel: 'silent'
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
return { pluginId, output, files: outputFiles.map((file) => file.path), inputs: Object.keys(result.metafile.inputs) }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
const args = process.argv.slice(2)
|
|
126
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
127
|
+
console.log('用法:ade-plugin-build [插件目录] [--entry src/renderer.tsx] [--main-entry src/main.ts]')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
let directory = '.'
|
|
131
|
+
let directorySeen = false
|
|
132
|
+
const options = {}
|
|
133
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
134
|
+
const arg = args[i]
|
|
135
|
+
if (arg === '--entry' || arg === '--main-entry') {
|
|
136
|
+
const value = args[++i]
|
|
137
|
+
if (!value || value.startsWith('--')) throw new Error(`${arg} 缺少文件路径`)
|
|
138
|
+
options[arg === '--entry' ? 'entry' : 'mainEntry'] = value
|
|
139
|
+
} else if (arg.startsWith('-')) throw new Error(`未知参数:${arg}`)
|
|
140
|
+
else if (!directorySeen) { directory = arg; directorySeen = true }
|
|
141
|
+
else throw new Error('只能指定一个插件目录')
|
|
142
|
+
}
|
|
143
|
+
const result = await buildPlugin(directory, options)
|
|
144
|
+
console.log(`已构建插件「${result.pluginId}」:${result.output}`)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// npm 的 .bin 命令是符号链接;Node 会将模块 URL 解析为真实文件地址。
|
|
148
|
+
const invokedPath = process.argv[1] ? await fs.realpath(path.resolve(process.argv[1])).catch(() => null) : null
|
|
149
|
+
if (invokedPath === modulePath) {
|
|
150
|
+
main().catch((error) => {
|
|
151
|
+
console.error(`插件构建失败:${error instanceof Error ? error.message : String(error)}`)
|
|
152
|
+
process.exitCode = 1
|
|
153
|
+
})
|
|
154
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type * as React from 'react'
|
|
2
|
+
import type * as ReactDOM from 'react-dom'
|
|
3
|
+
import type { AdeApi } from './types/api.js'
|
|
4
|
+
import type { PluginContext, SessionContext, ChatSession, ChatAdapter } from './types/adapter.js'
|
|
5
|
+
import type {
|
|
6
|
+
PluginCommandContext, PluginConfig, PluginDisposer, PluginSlotName, PluginViewInfo
|
|
7
|
+
} from './types/plugins.js'
|
|
8
|
+
|
|
9
|
+
export type * from './types/plugins.js'
|
|
10
|
+
export type * from './types/api.js'
|
|
11
|
+
export type * from './types/ade-types.js'
|
|
12
|
+
export type * from './types/adapter.js'
|
|
13
|
+
export type PluginManagementApi = AdeApi['plugins']
|
|
14
|
+
|
|
15
|
+
/** Component 在 ADE 的同一棵 React 树内运行。 */
|
|
16
|
+
export interface PluginComponentProps {
|
|
17
|
+
context: PluginCommandContext
|
|
18
|
+
config: PluginConfig
|
|
19
|
+
api: AdeApi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PluginSlotRegistration {
|
|
23
|
+
id: string
|
|
24
|
+
component: React.ComponentType<PluginComponentProps>
|
|
25
|
+
order?: number
|
|
26
|
+
when?: (context: PluginCommandContext) => boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PluginViewRegistration extends PluginViewInfo {
|
|
30
|
+
component: React.ComponentType<PluginComponentProps>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RendererPluginContext {
|
|
34
|
+
pluginId: string
|
|
35
|
+
React: typeof React
|
|
36
|
+
ReactDOM: typeof ReactDOM
|
|
37
|
+
api: AdeApi
|
|
38
|
+
subscriptions: PluginDisposer[]
|
|
39
|
+
getContext(): PluginCommandContext
|
|
40
|
+
useContext(): PluginCommandContext
|
|
41
|
+
getConfig(): PluginConfig
|
|
42
|
+
useConfig(): PluginConfig
|
|
43
|
+
setConfig(patch: PluginConfig): Promise<PluginConfig>
|
|
44
|
+
onConfigChanged(listener: (config: PluginConfig) => void): PluginDisposer
|
|
45
|
+
registerView(view: PluginViewRegistration): PluginDisposer
|
|
46
|
+
registerSlot(name: PluginSlotName, contribution: PluginSlotRegistration): PluginDisposer
|
|
47
|
+
registerCommand(id: string, handler: (args: unknown, context: PluginCommandContext) => unknown | Promise<unknown>): PluginDisposer
|
|
48
|
+
/** 优先执行本插件 renderer 命令,其次执行本插件 main 命令;跨插件使用 plugin:ID:command。 */
|
|
49
|
+
executeCommand(id: string, args?: unknown): Promise<unknown>
|
|
50
|
+
on(event: string, listener: (data: unknown) => void): PluginDisposer
|
|
51
|
+
emit(event: string, data?: unknown): void
|
|
52
|
+
provideService(name: string, value: unknown): PluginDisposer
|
|
53
|
+
getService<T = unknown>(name: string): T | undefined
|
|
54
|
+
/** 接收清理函数,而不是 effect setup;注册项已经由宿主自动管理。 */
|
|
55
|
+
effect(cleanup: PluginDisposer): PluginDisposer
|
|
56
|
+
addStyle(css: string): PluginDisposer
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type PluginSessionContext = SessionContext
|
|
60
|
+
export type PluginChatSession = ChatSession
|
|
61
|
+
export type PluginChatAdapter = ChatAdapter
|
|
62
|
+
export type MainPluginContext = PluginContext
|
|
63
|
+
|
|
64
|
+
export interface RendererPlugin {
|
|
65
|
+
activate(context: RendererPluginContext): void | PluginDisposer | Promise<void | PluginDisposer>
|
|
66
|
+
deactivate?(): void | Promise<void>
|
|
67
|
+
}
|
|
68
|
+
export interface MainPlugin {
|
|
69
|
+
activate(context: MainPluginContext): void | PluginDisposer | Promise<void | PluginDisposer>
|
|
70
|
+
deactivate?(): void | Promise<void>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 带类型推导的插件定义;不会执行插件。 */
|
|
74
|
+
export function definePlugin<T extends RendererPlugin | MainPlugin>(plugin: T): T
|
|
75
|
+
|
|
76
|
+
/** 渲染插件约定的子扩展位置;pluginId 可筛选其拥有者。 */
|
|
77
|
+
export const Slot: React.ComponentType<{ name: PluginSlotName; pluginId?: string }>
|
package/index.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai_mind/ade-plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "ADE 可信插件 SDK、类型与 React 插件构建工具",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "sepmini",
|
|
7
|
+
"engines": { "node": ">=18" },
|
|
8
|
+
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./index.mjs",
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"exports": { ".": { "types": "./index.d.ts", "default": "./index.mjs" } },
|
|
13
|
+
"bin": { "ade-plugin-build": "./cli/build.mjs" },
|
|
14
|
+
"files": ["index.mjs", "index.d.ts", "types", "cli", "README.md", "LICENSE"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"sync:types": "node scripts/sync-types.mjs",
|
|
17
|
+
"release:preflight": "node scripts/release-preflight.mjs",
|
|
18
|
+
"release:dry-run": "node scripts/release-preflight.mjs --dry-run",
|
|
19
|
+
"prepublishOnly": "node scripts/release-preflight.mjs"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": { "esbuild": "^0.25.0" },
|
|
22
|
+
"peerDependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" }
|
|
23
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// 由 sdk/scripts/sync-types.mjs 从 ADE 宿主生成,请勿手工修改。
|
|
2
|
+
import type { ChatAdapterInfo, ChatApprovalResponse, ChatAttachment, ChatAttachmentKind, ChatMessageRecord, CliModelOption, CliOption, StructuredEvent } from './ade-types.js';
|
|
3
|
+
import type { PluginCommandContext, PluginConfig, PluginDisposer } from './plugins.js';
|
|
4
|
+
export interface SessionContext {
|
|
5
|
+
id: string;
|
|
6
|
+
worktree: string;
|
|
7
|
+
cwd: string;
|
|
8
|
+
/** 会话所属 ADE 项目根目录;daemon 用于生成 Paseo projectKey。 */
|
|
9
|
+
projectPath?: string | null;
|
|
10
|
+
onData: (data: string) => void;
|
|
11
|
+
onExit: (code: number) => void;
|
|
12
|
+
onError: (err: Error) => void;
|
|
13
|
+
/**
|
|
14
|
+
* 底层 agent CLI 的会话标识(由 ChatManager 生成)。终端适配器用它
|
|
15
|
+
* 以固定会话启动/续接 TUI(如 qodercli --session-id / -r),使聊天视图
|
|
16
|
+
* 与终端视图共享同一会话、切换不丢内容。不支持时为 undefined。
|
|
17
|
+
*/
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
/** 是否为续接(该会话已有历史,spawn 时用 resume 而非创建) */
|
|
20
|
+
resume?: boolean;
|
|
21
|
+
/** 会话级 CLI 配置(用户在新会话对话框中选择的选项,如 --model) */
|
|
22
|
+
config?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
export interface ChatSession {
|
|
25
|
+
id: string;
|
|
26
|
+
write(data: string): void;
|
|
27
|
+
resize(cols: number, rows: number): void;
|
|
28
|
+
dispose(): void | Promise<void>;
|
|
29
|
+
/** 底层进程真正退出时完成;用于切换传输前等待文件锁/会话锁释放。 */
|
|
30
|
+
closed?: Promise<number>;
|
|
31
|
+
}
|
|
32
|
+
/** 结构化对话会话的上下文:除原始字节流外,额外提供解析后的事件通道 */
|
|
33
|
+
export interface StructuredSessionContext extends SessionContext {
|
|
34
|
+
onEvent: (ev: StructuredEvent) => void;
|
|
35
|
+
}
|
|
36
|
+
/** 启动一轮结构化对话的参数 */
|
|
37
|
+
export interface ChatTurnOptions {
|
|
38
|
+
prompt: string;
|
|
39
|
+
attachments?: ChatAttachment[];
|
|
40
|
+
/** CLI 会话标识:续接已有会话时传入(由 ChatManager 维护) */
|
|
41
|
+
sessionId: string | null;
|
|
42
|
+
/** 是否为续接(true 时用 resume 而非创建新会话) */
|
|
43
|
+
resume: boolean;
|
|
44
|
+
/** 会话级 CLI 配置(用户选择,如 --model / --permission-mode) */
|
|
45
|
+
config?: Record<string, string>;
|
|
46
|
+
}
|
|
47
|
+
/** provider 已持久化、可通过原生 session/thread id 继续的会话摘要。 */
|
|
48
|
+
export interface ProviderChatSession {
|
|
49
|
+
sessionId: string;
|
|
50
|
+
cwd: string;
|
|
51
|
+
name?: string;
|
|
52
|
+
preview?: string;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
updatedAt: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ChatAdapter {
|
|
57
|
+
id: string;
|
|
58
|
+
label: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
kind: 'terminal' | 'sdk';
|
|
61
|
+
/** Spawn command, used by terminal adapters and available to plugins */
|
|
62
|
+
command?: string;
|
|
63
|
+
/** Plugin id that registered this adapter */
|
|
64
|
+
plugin?: string;
|
|
65
|
+
/**
|
|
66
|
+
* 该 CLI 的「YOLO / 跳过权限确认」参数;未声明表示不支持(设置页不提供开关)。
|
|
67
|
+
* 设置里按 Agent 开启后,新建会话时由 ChatManager 并入会话配置。
|
|
68
|
+
*/
|
|
69
|
+
yoloFlag?: string;
|
|
70
|
+
/** 结构化协议是否原生流式输出(渲染层据此决定打字机动画) */
|
|
71
|
+
streaming?: boolean;
|
|
72
|
+
/** 构造 --help 的 argv(有协议的适配器提供,如 codex 需 exec --help) */
|
|
73
|
+
helpArgs?: () => string[];
|
|
74
|
+
/** 核心配置项(显示在对话配置条上;其余动态选项在「更多配置」) */
|
|
75
|
+
coreOptions?: string[];
|
|
76
|
+
/** 模型列表命令 argv(如 qodercli --list-models);无则模型自由输入 */
|
|
77
|
+
listModelsArgs?: () => string[];
|
|
78
|
+
/** 把 CLI 模型列表解析为实际 value、显示名与分组。 */
|
|
79
|
+
parseModels?: (output: string) => CliModelOption[];
|
|
80
|
+
/** 对 help 未声明 choices 的选项提供建议值(如 qoder --reasoning-effort) */
|
|
81
|
+
suggestions?: Record<string, string[]>;
|
|
82
|
+
/**
|
|
83
|
+
* 该协议自己拥有、不应出现在配置界面的原生参数(会话续接、工程、日志、输出 schema 等)。
|
|
84
|
+
* 按 adapter 作用域排除,避免为了某个 CLI 而在全局黑名单里误伤其他 CLI 的同名参数。
|
|
85
|
+
*/
|
|
86
|
+
excludedOptions?: string[];
|
|
87
|
+
/** 通过 provider 原生协议补充/覆盖配置元数据(例如 Codex model/list)。refresh 为 true 时忽略缓存重新拉取。 */
|
|
88
|
+
loadOptions?: (refresh?: boolean) => Promise<CliOption[]>;
|
|
89
|
+
createSession(ctx: SessionContext): ChatSession | Promise<ChatSession>;
|
|
90
|
+
/**
|
|
91
|
+
* 原生对话能力(可选):适配器支持结构化事件流(如 qodercli stream-json)
|
|
92
|
+
* 时实现它,聊天面板可渲染原生气泡界面。每轮对话由 ChatManager 调用一次,
|
|
93
|
+
* 进程输出经解析后通过 ctx.onEvent 广播;进程退出(或异常)调 onExit。
|
|
94
|
+
*/
|
|
95
|
+
chat?: {
|
|
96
|
+
/**
|
|
97
|
+
* 会话标识是否由应用预分配(qoder/claude/pi 支持 --session-id 创建指定
|
|
98
|
+
* 会话)。false 表示会话 id 由 CLI 生成(如 codex thread_id),首轮后
|
|
99
|
+
* 通过 init 事件回填,续接必须等回填完成。
|
|
100
|
+
*/
|
|
101
|
+
preassignedSessionId?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* 在首轮消息前预创建 provider 会话。返回的 id 只代表已分配;在首个 turn
|
|
104
|
+
* 持久化前,不能假定终端 CLI 已经可以 resume 它。
|
|
105
|
+
*/
|
|
106
|
+
prepareSession?: (ctx: StructuredSessionContext, config?: Record<string, string>) => Promise<{
|
|
107
|
+
sessionId: string;
|
|
108
|
+
}>;
|
|
109
|
+
/** 图形化连接让位给 TUI 前释放 provider 连接。 */
|
|
110
|
+
detach?: (id: string) => void | Promise<void>;
|
|
111
|
+
/** 从 provider 原生历史重建气泡消息(终端交互后回到图形界面时使用)。 */
|
|
112
|
+
readHistory?: (ctx: StructuredSessionContext, sessionId: string, config?: Record<string, string>) => Promise<ChatMessageRecord[]>;
|
|
113
|
+
/** 发现本地 CLI 已持久化、可 resume 的会话。 */
|
|
114
|
+
discoverSessions?: (cwds: string[]) => Promise<ProviderChatSession[]>;
|
|
115
|
+
/** 活跃 turn 支持追加指令时使用;不支持的适配器由 ChatManager 排队。 */
|
|
116
|
+
steer?: (id: string, prompt: string, attachments?: ChatAttachment[]) => Promise<void>;
|
|
117
|
+
/** 回复审批或运行时用户输入请求。 */
|
|
118
|
+
respond?: (id: string, requestId: string, response: ChatApprovalResponse) => Promise<void>;
|
|
119
|
+
/** 永久关闭适配器为该 ADE 会话保留的资源。 */
|
|
120
|
+
disposeSession?: (id: string) => void | Promise<void>;
|
|
121
|
+
/** 实际可发送的附件类型;目录始终作为提示词中的路径引用。 */
|
|
122
|
+
attachmentKinds?: ChatAttachmentKind[];
|
|
123
|
+
/** 旧插件兼容字段;未声明 attachmentKinds 时 true 视为支持 image/file。 */
|
|
124
|
+
attachmentSupported?: boolean;
|
|
125
|
+
/** 启动一轮结构化对话 */
|
|
126
|
+
startTurn(ctx: StructuredSessionContext, opts: ChatTurnOptions): ChatSession | Promise<ChatSession>;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export interface PluginHost {
|
|
130
|
+
registerAdapter(adapter: ChatAdapter): void;
|
|
131
|
+
log(level: 'info' | 'warn' | 'error', message: string): void;
|
|
132
|
+
}
|
|
133
|
+
/** 可信本地插件的主进程上下文;所有注册项在停用时自动撤销。 */
|
|
134
|
+
export interface PluginContext extends PluginHost {
|
|
135
|
+
pluginId: string;
|
|
136
|
+
pluginPath: string;
|
|
137
|
+
dataPath: string;
|
|
138
|
+
subscriptions: PluginDisposer[];
|
|
139
|
+
registerCommand(id: string, handler: (args: unknown, context: PluginCommandContext) => unknown | Promise<unknown>): PluginDisposer;
|
|
140
|
+
provideService(name: string, service: Record<string, unknown>): PluginDisposer;
|
|
141
|
+
getService(name: string): unknown;
|
|
142
|
+
events: {
|
|
143
|
+
on(name: string, listener: (payload: unknown) => void): PluginDisposer;
|
|
144
|
+
emit(name: string, payload?: unknown): void;
|
|
145
|
+
};
|
|
146
|
+
getConfig(): PluginConfig;
|
|
147
|
+
setConfig(patch: PluginConfig): Promise<PluginConfig>;
|
|
148
|
+
onConfigChanged(listener: (config: PluginConfig) => void): PluginDisposer;
|
|
149
|
+
/** 登记定时器、文件监听等资源的清理函数。 */
|
|
150
|
+
effect(disposer: PluginDisposer): PluginDisposer;
|
|
151
|
+
}
|
|
152
|
+
export type PluginMain = (host: PluginHost) => void | Promise<void>;
|
|
153
|
+
export declare function adapterToInfo(a: ChatAdapter): ChatAdapterInfo;
|