@coralai/sps-plugin-api 0.1.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 ADDED
@@ -0,0 +1,32 @@
1
+ # @coralai/sps-plugin-api
2
+
3
+ sps **宿主半区**的插件契约:`ctx` 上那些服务的方法签名。
4
+
5
+ ```bash
6
+ npm i -D @coralai/sps-plugin-api
7
+ ```
8
+
9
+ ```ts
10
+ import type { SpsPluginContext } from '@coralai/sps-plugin-api';
11
+
12
+ export const name = 'coral-plugin-x';
13
+ export const inject = ['sps.tools'];
14
+
15
+ export function apply(ctx: SpsPluginContext, config: { greeting?: string }) {
16
+ ctx.effect(() => // 🔴 注册一定要放进 effect,否则停用时收不干净
17
+ ctx['sps.tools'].register({
18
+ name: 'my_tool',
19
+ description: '给 agent 用的工具',
20
+ inputSchema: { type: 'object', properties: {} }, // 标准 JSON Schema
21
+ handler: async (args, session) => ({ content: [{ type: 'text', text: 'ok' }] }),
22
+ }),
23
+ );
24
+ }
25
+ ```
26
+
27
+ 这个包**只声明插件会调的那一面**。服务内部还有别的方法(注册表的 `list()` 之类),
28
+ 不写进来 —— 写进来就等于承诺它们不变。
29
+
30
+ sps-cli 仓里有一道一致性判据钉着这份契约:实现与它对不上,**sps-cli 自己的 check 会红**。
31
+
32
+ 完整写法见 sps-cli 仓的 `docs/guides/writing-plugins.md`。
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @coralai/sps-plugin-api —— **sps 宿主半区的插件契约**。
3
+ *
4
+ * 插件装它拿类型:`ctx['sps.tools']` 这些服务的方法签名。
5
+ *
6
+ * 🔴 **这个包是契约的真源,宿主去符合它** —— 不是反过来。
7
+ * 反过来(从宿主导出类型)的话,包永远滞后于实现,
8
+ * 而作者看到的类型和实际行为对不上时,他没法判断该信哪个。
9
+ * sps-cli 侧有一道一致性判据钉着:seam 与这里的接口对不上,**它自己的 check 会红**。
10
+ *
11
+ * ⚠️ 这里只声明**插件会调的那一面**。服务内部还有别的方法(比如注册表的
12
+ * `list()`),不写进来 —— 写进来就等于承诺它们不变,而那不是我们打算承诺的。
13
+ */
14
+ export interface SpsToolSession {
15
+ cwd: string;
16
+ project?: string;
17
+ agentId?: string;
18
+ }
19
+ export interface SpsToolResult {
20
+ content: {
21
+ type: 'text';
22
+ text: string;
23
+ }[];
24
+ isError?: boolean;
25
+ }
26
+ export interface SpsToolDef {
27
+ name: string;
28
+ description: string;
29
+ /** 标准 JSON Schema 的 object。宿主会转成 MCP 要的形状。 */
30
+ inputSchema: {
31
+ type: 'object';
32
+ properties?: Record<string, unknown>;
33
+ required?: string[];
34
+ };
35
+ handler: (args: Record<string, unknown>, session: SpsToolSession) => Promise<SpsToolResult>;
36
+ }
37
+ export interface SpsToolsRegistry {
38
+ /** 返回注销函数 —— **放进 `ctx.effect(...)`**,否则停用时收不干净。 */
39
+ register(def: SpsToolDef): () => void;
40
+ }
41
+ /** 凭据**引用**(环境变量名的形状),不是值。 */
42
+ export type CredentialRef = string;
43
+ export interface SpsCredentials {
44
+ /** 取真值。**每次操作现解**,别缓存到闭包里 —— 换密钥就是靠这一点不用重挂。 */
45
+ resolve(ref: CredentialRef): Promise<{
46
+ value: string;
47
+ source: string;
48
+ } | undefined>;
49
+ /** 给界面看的描述。**绝不含值。** */
50
+ describe(ref: CredentialRef): Promise<{
51
+ ref: string;
52
+ configured: boolean;
53
+ source?: string;
54
+ writable: boolean;
55
+ }>;
56
+ set(ref: CredentialRef, value: string): Promise<void>;
57
+ unset(ref: CredentialRef): Promise<void>;
58
+ }
59
+ export interface SpsWebRoute {
60
+ kind: 'exact' | 'prefix';
61
+ /** 相对路径。默认会前缀成 `/plugins/<你的id>/...`。 */
62
+ path: string;
63
+ handler: (req: unknown, res: unknown) => void | Promise<void>;
64
+ /**
65
+ * 接管宿主已有路径。允许,但**必须显式写出来**,而且会显示在插件页的
66
+ * 「接管了 N 条宿主路由」里 —— 能接管不可怕,接管了没人知道才可怕。
67
+ */
68
+ takeover?: boolean;
69
+ }
70
+ export interface SpsWebServer {
71
+ register(owner: string, route: SpsWebRoute): () => void;
72
+ }
73
+ export interface SpsSettingsScope<T = unknown> {
74
+ get(): T;
75
+ watch(cb: (next: T, prev: T) => void): () => void;
76
+ }
77
+ export interface SpsSettings {
78
+ /**
79
+ * 注册一个命名空间。**同名重复注册会抛。**
80
+ * `base` 一般就是 `apply(ctx, config)` 收到的那个 config。
81
+ */
82
+ register<T>(ns: string, schema?: unknown, options?: {
83
+ base?: T;
84
+ }): SpsSettingsScope<T>;
85
+ }
86
+ export interface SpsPromptSection {
87
+ /** 段名,重复会抛。 */
88
+ name: string;
89
+ /** 排序键,小的在前。内建规则是 0。 */
90
+ order: number;
91
+ /** 字符串,或每次组装现求值的函数。 */
92
+ text: string | (() => string);
93
+ }
94
+ export interface SpsSystemPrompt {
95
+ section(section: SpsPromptSection): () => void;
96
+ }
97
+ export interface SpsSpawnSpec {
98
+ argv: readonly string[];
99
+ cwd: string;
100
+ env?: Record<string, string | undefined>;
101
+ signal?: AbortSignal;
102
+ /** 收到取消后等它自己退出多久才 SIGKILL。默认 5000。 */
103
+ graceMs?: number;
104
+ }
105
+ export interface SpsSubprocess {
106
+ resolveExecutable(cmd: string): Promise<string | undefined>;
107
+ spawn(spec: SpsSpawnSpec): Promise<{
108
+ code: number | null;
109
+ stdout: string;
110
+ stderr: string;
111
+ }>;
112
+ }
113
+ export interface SpsImageRef {
114
+ /** `<sha256>.<ext>` —— 内容寻址,同一张图存两次是同一个 ref。 */
115
+ id: string;
116
+ mediaType: string;
117
+ }
118
+ export interface SpsAttachments {
119
+ validateImage(input: {
120
+ data: Uint8Array;
121
+ mediaType: string;
122
+ name?: string;
123
+ }): Promise<void>;
124
+ saveImage(input: {
125
+ data: Uint8Array;
126
+ mediaType: string;
127
+ name?: string;
128
+ }): Promise<SpsImageRef>;
129
+ readImage(ref: SpsImageRef): Promise<SpsImageRef & {
130
+ data: Uint8Array;
131
+ }>;
132
+ }
133
+ /** 插件 `inject` 里写的名字。 */
134
+ export declare const SPS_SERVICES: {
135
+ readonly tools: "sps.tools";
136
+ readonly credentials: "sps.credentials";
137
+ readonly webserver: "sps.webserver";
138
+ readonly settings: "sps.settings";
139
+ readonly systemPrompt: "sps.systemPrompt";
140
+ readonly subprocess: "sps.subprocess";
141
+ readonly attachments: "sps.attachments";
142
+ readonly media: "sps.media";
143
+ readonly im: "sps.im";
144
+ readonly capabilities: "sps.capabilities";
145
+ readonly agents: "sps.agents";
146
+ readonly repo: "sps.repo";
147
+ readonly memory: "sps.memory";
148
+ readonly notifier: "sps.notifier";
149
+ };
150
+ /** 插件 ctx 上能拿到的服务(只列已声明契约的那几个)。 */
151
+ export interface SpsPluginContext {
152
+ 'sps.tools': SpsToolsRegistry;
153
+ 'sps.credentials': SpsCredentials;
154
+ 'sps.webserver': SpsWebServer;
155
+ 'sps.settings': SpsSettings;
156
+ 'sps.systemPrompt': SpsSystemPrompt;
157
+ 'sps.subprocess': SpsSubprocess;
158
+ 'sps.attachments': SpsAttachments;
159
+ }
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @coralai/sps-plugin-api —— **sps 宿主半区的插件契约**。
3
+ *
4
+ * 插件装它拿类型:`ctx['sps.tools']` 这些服务的方法签名。
5
+ *
6
+ * 🔴 **这个包是契约的真源,宿主去符合它** —— 不是反过来。
7
+ * 反过来(从宿主导出类型)的话,包永远滞后于实现,
8
+ * 而作者看到的类型和实际行为对不上时,他没法判断该信哪个。
9
+ * sps-cli 侧有一道一致性判据钉着:seam 与这里的接口对不上,**它自己的 check 会红**。
10
+ *
11
+ * ⚠️ 这里只声明**插件会调的那一面**。服务内部还有别的方法(比如注册表的
12
+ * `list()`),不写进来 —— 写进来就等于承诺它们不变,而那不是我们打算承诺的。
13
+ */
14
+ // ── 服务名 ───────────────────────────────────────────────────────────────
15
+ /** 插件 `inject` 里写的名字。 */
16
+ export const SPS_SERVICES = {
17
+ tools: 'sps.tools',
18
+ credentials: 'sps.credentials',
19
+ webserver: 'sps.webserver',
20
+ settings: 'sps.settings',
21
+ systemPrompt: 'sps.systemPrompt',
22
+ subprocess: 'sps.subprocess',
23
+ attachments: 'sps.attachments',
24
+ media: 'sps.media',
25
+ im: 'sps.im',
26
+ capabilities: 'sps.capabilities',
27
+ agents: 'sps.agents',
28
+ repo: 'sps.repo',
29
+ memory: 'sps.memory',
30
+ notifier: 'sps.notifier',
31
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@coralai/sps-plugin-api",
3
+ "version": "0.1.0",
4
+ "description": "sps 宿主半区的插件契约:ctx 上那些服务的方法签名。插件装它拿类型。",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "keywords": [
19
+ "sps",
20
+ "coralai",
21
+ "plugin",
22
+ "seam",
23
+ "api"
24
+ ],
25
+ "license": "MIT",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc -p tsconfig.json"
31
+ }
32
+ }