@openacp/plugin-sdk 2026.326.2

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.
@@ -0,0 +1,6 @@
1
+ export type { OpenACPPlugin, PluginContext, PluginPermission, PluginStorage, InstallContext, MigrateContext, TerminalIO, SettingsAPI, } from '@openacp/cli';
2
+ export type { CommandDef, CommandArgs, CommandResponse, MenuOption, ListItem, } from '@openacp/cli';
3
+ export type { SecurityService, FileServiceInterface, NotificationService, UsageService, SpeechServiceInterface, TunnelServiceInterface, ContextService, } from '@openacp/cli';
4
+ export type { IChannelAdapter, OutgoingMessage, PermissionRequest, PermissionOption, NotificationMessage, AgentCommand, } from '@openacp/cli';
5
+ export { MessagingAdapter, StreamAdapter, BaseRenderer } from '@openacp/cli';
6
+ export { SendQueue, DraftManager, ToolCallTracker, ActivityTracker } from '@openacp/cli';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // Adapter base classes
2
+ export { MessagingAdapter, StreamAdapter, BaseRenderer } from '@openacp/cli';
3
+ // Adapter primitives
4
+ export { SendQueue, DraftManager, ToolCallTracker, ActivityTracker } from '@openacp/cli';
@@ -0,0 +1,14 @@
1
+ import type { SecurityService, FileServiceInterface, NotificationService, UsageService, SpeechServiceInterface, TunnelServiceInterface, ContextService } from '@openacp/cli';
2
+ /**
3
+ * Factory functions that create mock implementations of OpenACP service interfaces.
4
+ * Each returns an object matching the service contract with sensible defaults.
5
+ */
6
+ export declare const mockServices: {
7
+ security(overrides?: Partial<SecurityService>): SecurityService;
8
+ fileService(overrides?: Partial<FileServiceInterface>): FileServiceInterface;
9
+ notifications(overrides?: Partial<NotificationService>): NotificationService;
10
+ usage(overrides?: Partial<UsageService>): UsageService;
11
+ speech(overrides?: Partial<SpeechServiceInterface>): SpeechServiceInterface;
12
+ tunnel(overrides?: Partial<TunnelServiceInterface>): TunnelServiceInterface;
13
+ context(overrides?: Partial<ContextService>): ContextService;
14
+ };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Factory functions that create mock implementations of OpenACP service interfaces.
3
+ * Each returns an object matching the service contract with sensible defaults.
4
+ */
5
+ export const mockServices = {
6
+ security(overrides) {
7
+ return {
8
+ async checkAccess() { return { allowed: true }; },
9
+ async checkSessionLimit() { return { allowed: true }; },
10
+ async getUserRole() { return 'user'; },
11
+ ...overrides,
12
+ };
13
+ },
14
+ fileService(overrides) {
15
+ return {
16
+ async saveFile(_sessionId, fileName, _data, mimeType) {
17
+ return { type: 'file', filePath: `/tmp/${fileName}`, fileName, mimeType, size: 0 };
18
+ },
19
+ async resolveFile() { return null; },
20
+ async readTextFileWithRange() { return ''; },
21
+ extensionFromMime() { return '.bin'; },
22
+ async convertOggToWav(data) { return data; },
23
+ ...overrides,
24
+ };
25
+ },
26
+ notifications(overrides) {
27
+ return {
28
+ async notify() { },
29
+ async notifyAll() { },
30
+ ...overrides,
31
+ };
32
+ },
33
+ usage(overrides) {
34
+ return {
35
+ async trackUsage() { },
36
+ async checkBudget() { return { ok: true, percent: 0 }; },
37
+ async getSummary() {
38
+ return { period: 'today', totalTokens: 0, totalCost: 0, currency: 'USD', sessionCount: 0, recordCount: 0 };
39
+ },
40
+ ...overrides,
41
+ };
42
+ },
43
+ speech(overrides) {
44
+ return {
45
+ async textToSpeech() { return Buffer.alloc(0); },
46
+ async speechToText() { return ''; },
47
+ registerTTSProvider() { },
48
+ registerSTTProvider() { },
49
+ ...overrides,
50
+ };
51
+ },
52
+ tunnel(overrides) {
53
+ return {
54
+ getPublicUrl() { return 'http://localhost:0'; },
55
+ async start() { return 'http://localhost:0'; },
56
+ async stop() { },
57
+ getStore() {
58
+ return {
59
+ storeFile() { return null; },
60
+ storeDiff() { return null; },
61
+ };
62
+ },
63
+ fileUrl(id) { return `http://localhost:0/file/${id}`; },
64
+ diffUrl(id) { return `http://localhost:0/diff/${id}`; },
65
+ ...overrides,
66
+ };
67
+ },
68
+ context(overrides) {
69
+ return {
70
+ async buildContext() { return ''; },
71
+ registerProvider() { },
72
+ ...overrides,
73
+ };
74
+ },
75
+ };
@@ -0,0 +1,35 @@
1
+ import type { PluginContext, CommandDef, CommandResponse, OutgoingMessage } from '@openacp/cli';
2
+ export interface TestContextOpts {
3
+ pluginName: string;
4
+ pluginConfig?: Record<string, unknown>;
5
+ permissions?: string[];
6
+ services?: Record<string, unknown>;
7
+ }
8
+ export interface TestPluginContext extends PluginContext {
9
+ /** Services registered via registerService() */
10
+ registeredServices: Map<string, unknown>;
11
+ /** Commands registered via registerCommand() */
12
+ registeredCommands: Map<string, CommandDef>;
13
+ /** Middleware registered via registerMiddleware() */
14
+ registeredMiddleware: Array<{
15
+ hook: string;
16
+ opts: unknown;
17
+ }>;
18
+ /** Events emitted via emit() */
19
+ emittedEvents: Array<{
20
+ event: string;
21
+ payload: unknown;
22
+ }>;
23
+ /** Messages sent via sendMessage() */
24
+ sentMessages: Array<{
25
+ sessionId: string;
26
+ content: OutgoingMessage;
27
+ }>;
28
+ /** Dispatch a registered command by name */
29
+ executeCommand(name: string, args?: Partial<import('@openacp/cli').CommandArgs>): Promise<CommandResponse | void>;
30
+ }
31
+ /**
32
+ * Creates a test-friendly PluginContext for unit-testing plugins.
33
+ * All state is in-memory, logger is silent, services are pre-populated.
34
+ */
35
+ export declare function createTestContext(opts: TestContextOpts): TestPluginContext;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Creates a test-friendly PluginContext for unit-testing plugins.
3
+ * All state is in-memory, logger is silent, services are pre-populated.
4
+ */
5
+ export function createTestContext(opts) {
6
+ const storageData = new Map();
7
+ const eventHandlers = new Map();
8
+ const registeredServices = new Map();
9
+ const registeredCommands = new Map();
10
+ const registeredMiddleware = [];
11
+ const emittedEvents = [];
12
+ const sentMessages = [];
13
+ // Pre-populate services from opts
14
+ if (opts.services) {
15
+ for (const [name, impl] of Object.entries(opts.services)) {
16
+ registeredServices.set(name, impl);
17
+ }
18
+ }
19
+ const storage = {
20
+ async get(key) {
21
+ return storageData.get(key);
22
+ },
23
+ async set(key, value) {
24
+ storageData.set(key, value);
25
+ },
26
+ async delete(key) {
27
+ storageData.delete(key);
28
+ },
29
+ async list() {
30
+ return Array.from(storageData.keys());
31
+ },
32
+ getDataDir() {
33
+ return '/tmp/openacp-test-data';
34
+ },
35
+ };
36
+ const silentLog = {
37
+ trace() { },
38
+ debug() { },
39
+ info() { },
40
+ warn() { },
41
+ error() { },
42
+ fatal() { },
43
+ child() { return silentLog; },
44
+ };
45
+ const ctx = {
46
+ pluginName: opts.pluginName,
47
+ pluginConfig: opts.pluginConfig ?? {},
48
+ // Events
49
+ on(event, handler) {
50
+ if (!eventHandlers.has(event)) {
51
+ eventHandlers.set(event, new Set());
52
+ }
53
+ eventHandlers.get(event).add(handler);
54
+ },
55
+ off(event, handler) {
56
+ eventHandlers.get(event)?.delete(handler);
57
+ },
58
+ emit(event, payload) {
59
+ emittedEvents.push({ event, payload });
60
+ const handlers = eventHandlers.get(event);
61
+ if (handlers) {
62
+ for (const handler of handlers) {
63
+ handler(payload);
64
+ }
65
+ }
66
+ },
67
+ // Actions
68
+ registerMiddleware(hook, opts) {
69
+ registeredMiddleware.push({ hook, opts });
70
+ },
71
+ registerService(name, implementation) {
72
+ registeredServices.set(name, implementation);
73
+ },
74
+ getService(name) {
75
+ return registeredServices.get(name);
76
+ },
77
+ registerCommand(def) {
78
+ registeredCommands.set(def.name, def);
79
+ },
80
+ storage,
81
+ log: silentLog,
82
+ async sendMessage(sessionId, content) {
83
+ sentMessages.push({ sessionId, content });
84
+ },
85
+ // Kernel access stubs
86
+ sessions: {},
87
+ config: {},
88
+ eventBus: {},
89
+ core: {},
90
+ // Test-specific
91
+ registeredServices,
92
+ registeredCommands,
93
+ registeredMiddleware,
94
+ emittedEvents,
95
+ sentMessages,
96
+ async executeCommand(name, args) {
97
+ const cmd = registeredCommands.get(name);
98
+ if (!cmd) {
99
+ throw new Error(`Command not found: ${name}`);
100
+ }
101
+ const defaultArgs = {
102
+ raw: '',
103
+ sessionId: null,
104
+ channelId: 'test',
105
+ userId: 'test-user',
106
+ async reply() { },
107
+ ...args,
108
+ };
109
+ return cmd.handler(defaultArgs);
110
+ },
111
+ };
112
+ return ctx;
113
+ }
@@ -0,0 +1,20 @@
1
+ import type { InstallContext } from '@openacp/cli';
2
+ export interface TestInstallContextOpts {
3
+ pluginName: string;
4
+ legacyConfig?: Record<string, unknown>;
5
+ terminalResponses?: Record<string, unknown[]>;
6
+ }
7
+ interface TerminalCall {
8
+ method: string;
9
+ args: unknown;
10
+ }
11
+ /**
12
+ * Creates a test-friendly InstallContext for unit-testing plugin install/configure/uninstall.
13
+ * Terminal prompts are auto-answered from the provided responses map.
14
+ * Settings are stored in-memory.
15
+ */
16
+ export declare function createTestInstallContext(opts: TestInstallContextOpts): InstallContext & {
17
+ terminalCalls: TerminalCall[];
18
+ settingsData: Map<string, unknown>;
19
+ };
20
+ export {};
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Creates a test-friendly InstallContext for unit-testing plugin install/configure/uninstall.
3
+ * Terminal prompts are auto-answered from the provided responses map.
4
+ * Settings are stored in-memory.
5
+ */
6
+ export function createTestInstallContext(opts) {
7
+ const settingsData = new Map();
8
+ const terminalCalls = [];
9
+ const responseQueues = new Map();
10
+ // Deep-copy response queues so we don't mutate caller's arrays
11
+ if (opts.terminalResponses) {
12
+ for (const [method, responses] of Object.entries(opts.terminalResponses)) {
13
+ responseQueues.set(method, [...responses]);
14
+ }
15
+ }
16
+ function getNextResponse(method, args) {
17
+ terminalCalls.push({ method, args });
18
+ const queue = responseQueues.get(method);
19
+ if (queue && queue.length > 0) {
20
+ return queue.shift();
21
+ }
22
+ // Default responses by method type
23
+ switch (method) {
24
+ case 'text': return '';
25
+ case 'password': return '';
26
+ case 'confirm': return false;
27
+ case 'select': return undefined;
28
+ case 'multiselect': return [];
29
+ default: return undefined;
30
+ }
31
+ }
32
+ const terminal = {
33
+ async text(promptOpts) {
34
+ return getNextResponse('text', promptOpts);
35
+ },
36
+ async select(promptOpts) {
37
+ return getNextResponse('select', promptOpts);
38
+ },
39
+ async confirm(promptOpts) {
40
+ return getNextResponse('confirm', promptOpts);
41
+ },
42
+ async password(promptOpts) {
43
+ return getNextResponse('password', promptOpts);
44
+ },
45
+ async multiselect(promptOpts) {
46
+ return getNextResponse('multiselect', promptOpts);
47
+ },
48
+ log: {
49
+ info() { },
50
+ success() { },
51
+ warning() { },
52
+ error() { },
53
+ step() { },
54
+ },
55
+ spinner() {
56
+ return {
57
+ start() { },
58
+ stop() { },
59
+ fail() { },
60
+ };
61
+ },
62
+ note() { },
63
+ cancel() { },
64
+ };
65
+ const settings = {
66
+ async get(key) {
67
+ return settingsData.get(key);
68
+ },
69
+ async set(key, value) {
70
+ settingsData.set(key, value);
71
+ },
72
+ async getAll() {
73
+ return Object.fromEntries(settingsData);
74
+ },
75
+ async setAll(allSettings) {
76
+ settingsData.clear();
77
+ for (const [k, v] of Object.entries(allSettings)) {
78
+ settingsData.set(k, v);
79
+ }
80
+ },
81
+ async delete(key) {
82
+ settingsData.delete(key);
83
+ },
84
+ async clear() {
85
+ settingsData.clear();
86
+ },
87
+ async has(key) {
88
+ return settingsData.has(key);
89
+ },
90
+ };
91
+ const silentLog = {
92
+ trace() { },
93
+ debug() { },
94
+ info() { },
95
+ warn() { },
96
+ error() { },
97
+ fatal() { },
98
+ child() { return silentLog; },
99
+ };
100
+ return {
101
+ pluginName: opts.pluginName,
102
+ terminal,
103
+ settings,
104
+ legacyConfig: opts.legacyConfig,
105
+ dataDir: '/tmp/openacp-test-data',
106
+ log: silentLog,
107
+ // Test-specific
108
+ terminalCalls,
109
+ settingsData,
110
+ };
111
+ }
@@ -0,0 +1,5 @@
1
+ export { createTestContext } from './testing/test-context.js';
2
+ export type { TestPluginContext, TestContextOpts } from './testing/test-context.js';
3
+ export { createTestInstallContext } from './testing/test-install-context.js';
4
+ export type { TestInstallContextOpts } from './testing/test-install-context.js';
5
+ export { mockServices } from './testing/mock-services.js';
@@ -0,0 +1,3 @@
1
+ export { createTestContext } from './testing/test-context.js';
2
+ export { createTestInstallContext } from './testing/test-install-context.js';
3
+ export { mockServices } from './testing/mock-services.js';
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@openacp/plugin-sdk",
3
+ "version": "2026.0326.2",
4
+ "description": "SDK for building OpenACP plugins — types, base classes, and testing utilities",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./testing": {
12
+ "types": "./dist/testing.d.ts",
13
+ "import": "./dist/testing.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist/"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "test": "vitest run"
22
+ },
23
+ "peerDependencies": {
24
+ "@openacp/cli": ">=0.6.0"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/Open-ACP/OpenACP",
29
+ "directory": "packages/plugin-sdk"
30
+ },
31
+ "homepage": "https://github.com/Open-ACP/OpenACP",
32
+ "keywords": [
33
+ "openacp",
34
+ "plugin",
35
+ "sdk",
36
+ "testing"
37
+ ],
38
+ "license": "MIT"
39
+ }