@openacp/plugin-sdk 2026.41.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/config.d.ts +2 -0
- package/dist/config.js +1 -0
- package/dist/formatting.d.ts +2 -0
- package/dist/formatting.js +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +20 -0
- package/dist/speech-types.d.ts +36 -0
- package/dist/speech-types.js +4 -0
- package/dist/testing/adapter-conformance.d.ts +2 -0
- package/dist/testing/adapter-conformance.js +64 -0
- package/dist/testing/mock-services.d.ts +14 -0
- package/dist/testing/mock-services.js +74 -0
- package/dist/testing/test-context.d.ts +36 -0
- package/dist/testing/test-context.js +114 -0
- package/dist/testing/test-install-context.d.ts +20 -0
- package/dist/testing/test-install-context.js +111 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.js +4 -0
- package/package.json +47 -0
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DoctorEngine, getSafeFields, resolveOptions, getConfigValue, isHotReloadable } from '@openacp/cli';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { DisplayVerbosity, ToolCallMeta, ToolUpdateMeta, ViewerLinks } from '@openacp/cli';
|
|
2
|
+
export { STATUS_ICONS, KIND_ICONS, progressBar, formatTokens, truncateContent, stripCodeFences, splitMessage, extractContentText, formatToolSummary, formatToolTitle, resolveToolIcon, } from '@openacp/cli';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { STATUS_ICONS, KIND_ICONS, progressBar, formatTokens, truncateContent, stripCodeFences, splitMessage, extractContentText, formatToolSummary, formatToolTitle, resolveToolIcon, } from '@openacp/cli';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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, TunnelServiceInterface, ContextService, } from '@openacp/cli';
|
|
4
|
+
export type { TTSProvider, TTSOptions, TTSResult, STTProvider, STTOptions, STTResult, SpeechServiceInterface, } from './speech-types.js';
|
|
5
|
+
export type { IChannelAdapter, AdapterCapabilities, OutgoingMessage, PermissionRequest, PermissionOption, NotificationMessage, AgentCommand, MessagingAdapterConfig, IRenderer, RenderedMessage, } from '@openacp/cli';
|
|
6
|
+
export { MessagingAdapter, StreamAdapter, BaseRenderer } from '@openacp/cli';
|
|
7
|
+
export { SendQueue, DraftManager, ToolCallTracker, ActivityTracker } from '@openacp/cli';
|
|
8
|
+
export { ToolStateMap, ThoughtBuffer } from '@openacp/cli';
|
|
9
|
+
export { DisplaySpecBuilder } from '@openacp/cli';
|
|
10
|
+
export { OutputModeResolver } from '@openacp/cli';
|
|
11
|
+
export { ToolCardState } from '@openacp/cli';
|
|
12
|
+
export type { OpenACPCore, Session, SessionEvents, SessionManager, CommandRegistry, Attachment, PlanEntry, StopReason, SessionStatus, ConfigOption, UsageRecord, InstallProgress, DisplayVerbosity, ToolCallMeta, ToolUpdateMeta, ViewerLinks, TelegramPlatformData, } from '@openacp/cli';
|
|
13
|
+
export type { ToolDisplaySpec, ThoughtDisplaySpec, ToolEntry, OutputMode, ToolCardSnapshot, ToolCardStateConfig, } from '@openacp/cli';
|
|
14
|
+
export { log, createChildLogger } from '@openacp/cli';
|
|
15
|
+
export { PRODUCT_GUIDE } from '@openacp/cli';
|
|
16
|
+
export type { ConfigFieldDef, DoctorReport, PendingFix } from './config.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// @openacp/plugin-sdk — main entry point
|
|
3
|
+
//
|
|
4
|
+
// Sub-path imports available:
|
|
5
|
+
// @openacp/plugin-sdk/formatting — format utils, icons
|
|
6
|
+
// @openacp/plugin-sdk/config — config utils, doctor engine
|
|
7
|
+
// @openacp/plugin-sdk/testing — test helpers, conformance tests
|
|
8
|
+
// ============================================================
|
|
9
|
+
// --- Adapter base classes (runtime) ---
|
|
10
|
+
export { MessagingAdapter, StreamAdapter, BaseRenderer } from '@openacp/cli';
|
|
11
|
+
// --- Adapter primitives (runtime) ---
|
|
12
|
+
export { SendQueue, DraftManager, ToolCallTracker, ActivityTracker } from '@openacp/cli';
|
|
13
|
+
export { ToolStateMap, ThoughtBuffer } from '@openacp/cli';
|
|
14
|
+
export { DisplaySpecBuilder } from '@openacp/cli';
|
|
15
|
+
export { OutputModeResolver } from '@openacp/cli';
|
|
16
|
+
export { ToolCardState } from '@openacp/cli';
|
|
17
|
+
// --- Logging (runtime) ---
|
|
18
|
+
export { log, createChildLogger } from '@openacp/cli';
|
|
19
|
+
// --- Data (runtime) ---
|
|
20
|
+
export { PRODUCT_GUIDE } from '@openacp/cli';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface TTSOptions {
|
|
2
|
+
language?: string;
|
|
3
|
+
voice?: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TTSResult {
|
|
7
|
+
audioBuffer: Buffer;
|
|
8
|
+
mimeType: string;
|
|
9
|
+
}
|
|
10
|
+
export interface STTOptions {
|
|
11
|
+
language?: string;
|
|
12
|
+
model?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface STTResult {
|
|
15
|
+
text: string;
|
|
16
|
+
language?: string;
|
|
17
|
+
duration?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface TTSProvider {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
synthesize(text: string, options?: TTSOptions): Promise<TTSResult>;
|
|
22
|
+
}
|
|
23
|
+
export interface STTProvider {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
transcribe(audioBuffer: Buffer, mimeType: string, options?: STTOptions): Promise<STTResult>;
|
|
26
|
+
}
|
|
27
|
+
export interface SpeechServiceInterface {
|
|
28
|
+
registerTTSProvider(name: string, provider: TTSProvider): void;
|
|
29
|
+
unregisterTTSProvider(name: string): void;
|
|
30
|
+
registerSTTProvider(name: string, provider: STTProvider): void;
|
|
31
|
+
unregisterSTTProvider?(name: string): void;
|
|
32
|
+
isTTSAvailable(): boolean;
|
|
33
|
+
isSTTAvailable(): boolean;
|
|
34
|
+
synthesize(text: string, options?: TTSOptions): Promise<TTSResult>;
|
|
35
|
+
transcribe(audioBuffer: Buffer, mimeType: string, options?: STTOptions): Promise<STTResult>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
export function runAdapterConformanceTests(createAdapter, cleanup) {
|
|
3
|
+
let adapter;
|
|
4
|
+
afterEach(async () => {
|
|
5
|
+
await cleanup?.();
|
|
6
|
+
});
|
|
7
|
+
describe('IChannelAdapter conformance', () => {
|
|
8
|
+
it('has a name', async () => {
|
|
9
|
+
adapter = await createAdapter();
|
|
10
|
+
expect(typeof adapter.name).toBe('string');
|
|
11
|
+
expect(adapter.name.length).toBeGreaterThan(0);
|
|
12
|
+
});
|
|
13
|
+
it('declares capabilities correctly', async () => {
|
|
14
|
+
adapter = await createAdapter();
|
|
15
|
+
const caps = adapter.capabilities;
|
|
16
|
+
expect(typeof caps.streaming).toBe('boolean');
|
|
17
|
+
expect(typeof caps.richFormatting).toBe('boolean');
|
|
18
|
+
expect(typeof caps.threads).toBe('boolean');
|
|
19
|
+
expect(typeof caps.reactions).toBe('boolean');
|
|
20
|
+
expect(typeof caps.fileUpload).toBe('boolean');
|
|
21
|
+
expect(typeof caps.voice).toBe('boolean');
|
|
22
|
+
});
|
|
23
|
+
it('sends text messages without error', async () => {
|
|
24
|
+
adapter = await createAdapter();
|
|
25
|
+
await expect(adapter.sendMessage('test-session', { type: 'text', text: 'hello' })).resolves.not.toThrow();
|
|
26
|
+
});
|
|
27
|
+
it('sends tool_call messages without error', async () => {
|
|
28
|
+
adapter = await createAdapter();
|
|
29
|
+
await expect(adapter.sendMessage('test-session', {
|
|
30
|
+
type: 'tool_call',
|
|
31
|
+
text: 'Read',
|
|
32
|
+
metadata: { id: 't1', name: 'Read', kind: 'read' },
|
|
33
|
+
})).resolves.not.toThrow();
|
|
34
|
+
});
|
|
35
|
+
it('sends usage messages without error', async () => {
|
|
36
|
+
adapter = await createAdapter();
|
|
37
|
+
await expect(adapter.sendMessage('test-session', {
|
|
38
|
+
type: 'usage',
|
|
39
|
+
text: '',
|
|
40
|
+
metadata: { tokensUsed: 1000, contextSize: 200000 },
|
|
41
|
+
})).resolves.not.toThrow();
|
|
42
|
+
});
|
|
43
|
+
it('sends error messages without error', async () => {
|
|
44
|
+
adapter = await createAdapter();
|
|
45
|
+
await expect(adapter.sendMessage('test-session', { type: 'error', text: 'something failed' })).resolves.not.toThrow();
|
|
46
|
+
});
|
|
47
|
+
it('handles session_end without error', async () => {
|
|
48
|
+
adapter = await createAdapter();
|
|
49
|
+
await expect(adapter.sendMessage('test-session', { type: 'session_end', text: 'finished' })).resolves.not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
it('handles unknown message types gracefully', async () => {
|
|
52
|
+
adapter = await createAdapter();
|
|
53
|
+
await expect(adapter.sendMessage('test-session', { type: 'unknown_type', text: '' })).resolves.not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
it('sendNotification does not throw', async () => {
|
|
56
|
+
adapter = await createAdapter();
|
|
57
|
+
await expect(adapter.sendNotification({
|
|
58
|
+
sessionId: 'test',
|
|
59
|
+
type: 'completed',
|
|
60
|
+
summary: 'done',
|
|
61
|
+
})).resolves.not.toThrow();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -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,74 @@
|
|
|
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
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
speech(overrides) {
|
|
41
|
+
return {
|
|
42
|
+
async textToSpeech() { return Buffer.alloc(0); },
|
|
43
|
+
async speechToText() { return ''; },
|
|
44
|
+
registerTTSProvider() { },
|
|
45
|
+
registerSTTProvider() { },
|
|
46
|
+
...overrides,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
tunnel(overrides) {
|
|
50
|
+
return {
|
|
51
|
+
getPublicUrl() { return 'http://localhost:0'; },
|
|
52
|
+
async start() { return 'http://localhost:0'; },
|
|
53
|
+
async stop() { },
|
|
54
|
+
getStore() {
|
|
55
|
+
return {
|
|
56
|
+
storeFile() { return null; },
|
|
57
|
+
storeDiff() { return null; },
|
|
58
|
+
storeOutput() { return null; },
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
fileUrl(id) { return `http://localhost:0/file/${id}`; },
|
|
62
|
+
diffUrl(id) { return `http://localhost:0/diff/${id}`; },
|
|
63
|
+
outputUrl(id) { return `http://localhost:0/output/${id}`; },
|
|
64
|
+
...overrides,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
context(overrides) {
|
|
68
|
+
return {
|
|
69
|
+
async buildContext() { return ''; },
|
|
70
|
+
registerProvider() { },
|
|
71
|
+
...overrides,
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
instanceRoot?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TestPluginContext extends PluginContext {
|
|
10
|
+
/** Services registered via registerService() */
|
|
11
|
+
registeredServices: Map<string, unknown>;
|
|
12
|
+
/** Commands registered via registerCommand() */
|
|
13
|
+
registeredCommands: Map<string, CommandDef>;
|
|
14
|
+
/** Middleware registered via registerMiddleware() */
|
|
15
|
+
registeredMiddleware: Array<{
|
|
16
|
+
hook: string;
|
|
17
|
+
opts: unknown;
|
|
18
|
+
}>;
|
|
19
|
+
/** Events emitted via emit() */
|
|
20
|
+
emittedEvents: Array<{
|
|
21
|
+
event: string;
|
|
22
|
+
payload: unknown;
|
|
23
|
+
}>;
|
|
24
|
+
/** Messages sent via sendMessage() */
|
|
25
|
+
sentMessages: Array<{
|
|
26
|
+
sessionId: string;
|
|
27
|
+
content: OutgoingMessage;
|
|
28
|
+
}>;
|
|
29
|
+
/** Dispatch a registered command by name */
|
|
30
|
+
executeCommand(name: string, args?: Partial<import('@openacp/cli').CommandArgs>): Promise<CommandResponse | void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates a test-friendly PluginContext for unit-testing plugins.
|
|
34
|
+
* All state is in-memory, logger is silent, services are pre-populated.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createTestContext(opts: TestContextOpts): TestPluginContext;
|
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
instanceRoot: opts.instanceRoot ?? '/tmp/openacp-test',
|
|
49
|
+
// Events
|
|
50
|
+
on(event, handler) {
|
|
51
|
+
if (!eventHandlers.has(event)) {
|
|
52
|
+
eventHandlers.set(event, new Set());
|
|
53
|
+
}
|
|
54
|
+
eventHandlers.get(event).add(handler);
|
|
55
|
+
},
|
|
56
|
+
off(event, handler) {
|
|
57
|
+
eventHandlers.get(event)?.delete(handler);
|
|
58
|
+
},
|
|
59
|
+
emit(event, payload) {
|
|
60
|
+
emittedEvents.push({ event, payload });
|
|
61
|
+
const handlers = eventHandlers.get(event);
|
|
62
|
+
if (handlers) {
|
|
63
|
+
for (const handler of handlers) {
|
|
64
|
+
handler(payload);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
// Actions
|
|
69
|
+
registerMiddleware(hook, opts) {
|
|
70
|
+
registeredMiddleware.push({ hook, opts });
|
|
71
|
+
},
|
|
72
|
+
registerService(name, implementation) {
|
|
73
|
+
registeredServices.set(name, implementation);
|
|
74
|
+
},
|
|
75
|
+
getService(name) {
|
|
76
|
+
return registeredServices.get(name);
|
|
77
|
+
},
|
|
78
|
+
registerCommand(def) {
|
|
79
|
+
registeredCommands.set(def.name, def);
|
|
80
|
+
},
|
|
81
|
+
storage,
|
|
82
|
+
log: silentLog,
|
|
83
|
+
async sendMessage(sessionId, content) {
|
|
84
|
+
sentMessages.push({ sessionId, content });
|
|
85
|
+
},
|
|
86
|
+
// Kernel access stubs
|
|
87
|
+
sessions: {},
|
|
88
|
+
config: {},
|
|
89
|
+
eventBus: {},
|
|
90
|
+
core: {},
|
|
91
|
+
// Test-specific
|
|
92
|
+
registeredServices,
|
|
93
|
+
registeredCommands,
|
|
94
|
+
registeredMiddleware,
|
|
95
|
+
emittedEvents,
|
|
96
|
+
sentMessages,
|
|
97
|
+
async executeCommand(name, args) {
|
|
98
|
+
const cmd = registeredCommands.get(name);
|
|
99
|
+
if (!cmd) {
|
|
100
|
+
throw new Error(`Command not found: ${name}`);
|
|
101
|
+
}
|
|
102
|
+
const defaultArgs = {
|
|
103
|
+
raw: '',
|
|
104
|
+
sessionId: null,
|
|
105
|
+
channelId: 'test',
|
|
106
|
+
userId: 'test-user',
|
|
107
|
+
async reply() { },
|
|
108
|
+
...args,
|
|
109
|
+
};
|
|
110
|
+
return cmd.handler(defaultArgs);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return ctx;
|
|
114
|
+
}
|
|
@@ -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,6 @@
|
|
|
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';
|
|
6
|
+
export { runAdapterConformanceTests } from '@openacp/cli/testing';
|
package/dist/testing.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openacp/plugin-sdk",
|
|
3
|
+
"version": "2026.41.1",
|
|
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
|
+
"./formatting": {
|
|
12
|
+
"types": "./dist/formatting.d.ts",
|
|
13
|
+
"import": "./dist/formatting.js"
|
|
14
|
+
},
|
|
15
|
+
"./config": {
|
|
16
|
+
"types": "./dist/config.d.ts",
|
|
17
|
+
"import": "./dist/config.js"
|
|
18
|
+
},
|
|
19
|
+
"./testing": {
|
|
20
|
+
"types": "./dist/testing.d.ts",
|
|
21
|
+
"import": "./dist/testing.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"test": "vitest run"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@openacp/cli": ">=0.6.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/Open-ACP/OpenACP",
|
|
37
|
+
"directory": "packages/plugin-sdk"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/Open-ACP/OpenACP",
|
|
40
|
+
"keywords": [
|
|
41
|
+
"openacp",
|
|
42
|
+
"plugin",
|
|
43
|
+
"sdk",
|
|
44
|
+
"testing"
|
|
45
|
+
],
|
|
46
|
+
"license": "MIT"
|
|
47
|
+
}
|