@openforge-app/plugin-sdk 0.2.3 → 0.2.4
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 +1 -7
- package/dist/backend.d.ts +2 -2
- package/dist/frontend.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/manifest.d.ts +53 -4
- package/dist/manifest.js +18 -1
- package/dist/openforgePackageMetadataSchema.json +41 -4
- package/dist/pluginIconContract.d.ts +3 -0
- package/dist/pluginIconContract.js +12 -0
- package/dist/pluginIcons.js +1 -6
- package/dist/publicUiExports.mjs +1 -0
- package/dist/testing/commonApiFake.d.ts +2 -1
- package/dist/testing/commonApiFake.js +33 -1
- package/dist/testing/contracts.d.ts +6 -1
- package/dist/testing/frontendContributionFake.js +9 -0
- package/dist/testing/registryFake.js +1 -1
- package/dist/testing/support.js +5 -0
- package/dist/types.d.ts +59 -3
- package/dist/types.js +39 -0
- package/dist/ui/PluginSidebarLink.svelte +54 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @openforge-app/plugin-sdk
|
|
2
2
|
|
|
3
|
-
Public SDK for building OpenForge plugins.
|
|
3
|
+
Public SDK for building OpenForge plugins. [`docs/plugin-authoring.md`](../../docs/plugin-authoring.md) is the canonical guide for the package, runtime, Svelte-sharing, and CSS-loading contract.
|
|
4
4
|
|
|
5
5
|
Styled frontend plugins must declare Vite/Svelte's emitted CSS artifacts in `package.json#openforge.frontendStyles`:
|
|
6
6
|
|
|
@@ -24,9 +24,3 @@ Automated releases use npm trusted publishing rather than a long-lived npm token
|
|
|
24
24
|
`@openforge-app/plugin-sdk` is licensed under the MIT License. Plugin authors may use it for personal, internal, open source, or commercial plugin development, including redistribution and modification under the MIT terms.
|
|
25
25
|
|
|
26
26
|
This MIT license applies to the SDK package only. The OpenForge desktop application is licensed separately under the repository root `LICENSE` and is source-available/proprietary; the app may not be commercially resold or redistributed without permission.
|
|
27
|
-
|
|
28
|
-
## Current contract
|
|
29
|
-
|
|
30
|
-
Version `0.2.1` publishes the API-v1 contract used by the OpenForge host, including agent-facing Plugin Command metadata, `PluginCommandInvocationContext`, and the `@openforge-app/plugin-sdk/testing` helpers. Legacy handoff-summary declarations (`Task.summary` and `tasks.updateSummary`) are no longer part of the contract.
|
|
31
|
-
|
|
32
|
-
Desktop releases publish the SDK in the same workflow using the desktop git tag's semver; the manual publish workflow uses the checked-in package version. Both paths run `pnpm check:contract`, which packs the package, verifies all exported files, and compiles the current authoring contract against the tarball before publication.
|
package/dist/backend.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot } from './types';
|
|
1
|
+
import type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot } from './types';
|
|
2
2
|
export declare function defineBackendPlugin<const TPlugin extends BackendPlugin>(plugin: TPlugin): TPlugin;
|
|
3
|
-
export type { BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
|
3
|
+
export type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
2
|
-
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol } from './types';
|
|
2
|
+
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol } from './types';
|
|
3
3
|
export declare const OPENFORGE_FRONTEND_PLUGIN_MARKER = "__openforgeFrontendPlugin";
|
|
4
4
|
export type MarkedFrontendPlugin<TPlugin extends FrontendPlugin = FrontendPlugin> = TPlugin & {
|
|
5
5
|
readonly [OPENFORGE_FRONTEND_PLUGIN_MARKER]: true;
|
|
@@ -7,4 +7,4 @@ export type MarkedFrontendPlugin<TPlugin extends FrontendPlugin = FrontendPlugin
|
|
|
7
7
|
export declare function defineFrontendPlugin<const TPlugin extends FrontendPlugin>(plugin: TPlugin): MarkedFrontendPlugin<TPlugin>;
|
|
8
8
|
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
9
9
|
export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, };
|
|
10
|
-
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, };
|
|
10
|
+
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSur
|
|
|
3
3
|
export { OPENFORGE_PACKAGE_METADATA_SCHEMA, OPENFORGE_PLUGIN_CAPABILITIES, isOpenForgePackageMetadata, isPluginPackageMetadata, isSupportedOpenForgeApiVersion, validateOpenForgePackageMetadata, validatePluginPackageMetadata, } from './manifest';
|
|
4
4
|
export { createMemoryPluginStorage, createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, createTestingCalls, TestingOpenForgeRegistryFake, TestingSubscriptionSink, } from './testing';
|
|
5
5
|
export { TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
6
|
-
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, FileSystemAPI, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginCommandInvocationContext, PluginCommandInvocationSource, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginIcon, PluginInjectionPointProps, PluginSettingsSectionProps, PluginState, PluginSvgIcon, PluginTaskPaneProps, PluginTaskUISectionProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, ShellAPI, ShellResizeRequest, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, ShellSessionRequest, ShellSpawnRequest, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
6
|
+
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginCommandInvocationContext, PluginCommandInvocationSource, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginSettingsSectionProps, PluginState, PluginSvgIcon, PluginTaskPaneProps, PluginTaskUISectionProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, PtyBufferState, ShellAPI, ShellResizeRequest, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, ShellSessionRequest, ShellSpawnRequest, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
7
7
|
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingEventListenerContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing';
|
|
8
8
|
export { parseStrictFiniteNumber } from './numberParsing';
|
|
9
9
|
export { buildProjectFileTree, flattenVisibleProjectFileTree, formatProjectFileTreeSize, getProjectFileTreeDepth, getProjectFileTreeItemAccessibility, getProjectFileTreeKeyboardAction, getProjectFileTreeParentPath, hasProjectFileTreeShortcutModifier, projectFileTreePathToId, } from './projectFileTree';
|
package/dist/manifest.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
11
11
|
dependentRequired: {
|
|
12
12
|
frontendStyles: string[];
|
|
13
13
|
};
|
|
14
|
-
allOf: {
|
|
14
|
+
allOf: ({
|
|
15
15
|
if: {
|
|
16
16
|
properties: {
|
|
17
17
|
requires: {
|
|
@@ -19,13 +19,35 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
19
19
|
const: string;
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
+
enablement?: undefined;
|
|
22
23
|
};
|
|
23
24
|
required: string[];
|
|
24
25
|
};
|
|
25
26
|
then: {
|
|
26
27
|
required: string[];
|
|
28
|
+
properties?: undefined;
|
|
27
29
|
};
|
|
28
|
-
}
|
|
30
|
+
} | {
|
|
31
|
+
if: {
|
|
32
|
+
properties: {
|
|
33
|
+
enablement: {
|
|
34
|
+
const: string;
|
|
35
|
+
};
|
|
36
|
+
requires?: undefined;
|
|
37
|
+
};
|
|
38
|
+
required: string[];
|
|
39
|
+
};
|
|
40
|
+
then: {
|
|
41
|
+
properties: {
|
|
42
|
+
requires: {
|
|
43
|
+
contains: {
|
|
44
|
+
const: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
required: string[];
|
|
49
|
+
};
|
|
50
|
+
})[];
|
|
29
51
|
properties: {
|
|
30
52
|
id: {
|
|
31
53
|
type: string;
|
|
@@ -44,9 +66,36 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
44
66
|
type: string;
|
|
45
67
|
minLength: number;
|
|
46
68
|
};
|
|
69
|
+
enablement: {
|
|
70
|
+
enum: string[];
|
|
71
|
+
default: string;
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
47
74
|
icon: {
|
|
48
|
-
|
|
49
|
-
|
|
75
|
+
oneOf: ({
|
|
76
|
+
type: string;
|
|
77
|
+
minLength: number;
|
|
78
|
+
pattern: string;
|
|
79
|
+
additionalProperties?: undefined;
|
|
80
|
+
required?: undefined;
|
|
81
|
+
properties?: undefined;
|
|
82
|
+
} | {
|
|
83
|
+
type: string;
|
|
84
|
+
additionalProperties: boolean;
|
|
85
|
+
required: string[];
|
|
86
|
+
properties: {
|
|
87
|
+
type: {
|
|
88
|
+
const: string;
|
|
89
|
+
};
|
|
90
|
+
svg: {
|
|
91
|
+
type: string;
|
|
92
|
+
minLength: number;
|
|
93
|
+
pattern: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
minLength?: undefined;
|
|
97
|
+
pattern?: undefined;
|
|
98
|
+
})[];
|
|
50
99
|
description: string;
|
|
51
100
|
};
|
|
52
101
|
frontend: {
|
package/dist/manifest.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import packageMetadataSchemaData from './openforgePackageMetadataSchema.json';
|
|
2
|
+
import { isPluginIconName, isPluginSvgIcon } from './pluginIconContract';
|
|
2
3
|
import { SUPPORTED_OPENFORGE_API_VERSIONS } from './types';
|
|
3
4
|
export const OPENFORGE_PACKAGE_METADATA_SCHEMA = packageMetadataSchemaData;
|
|
4
5
|
export const OPENFORGE_PLUGIN_CAPABILITIES = packageMetadataSchemaData.properties.requires.items.enum;
|
|
@@ -27,6 +28,18 @@ function validateOptionalString(value, path) {
|
|
|
27
28
|
}
|
|
28
29
|
return [];
|
|
29
30
|
}
|
|
31
|
+
function validateEnablement(value) {
|
|
32
|
+
if (value === undefined || value === 'app' || value === 'project') {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return [{ path: 'enablement', message: 'Must be "app" or "project"' }];
|
|
36
|
+
}
|
|
37
|
+
function validatePluginIcon(value) {
|
|
38
|
+
if (value === undefined || isPluginIconName(value) || isPluginSvgIcon(value)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return [{ path: 'icon', message: 'Must be a non-empty Lucide icon name or { type: "svg", svg }' }];
|
|
42
|
+
}
|
|
30
43
|
function validateFrontendStyles(value) {
|
|
31
44
|
if (value === undefined)
|
|
32
45
|
return [];
|
|
@@ -97,7 +110,8 @@ export function validateOpenForgePackageMetadata(data) {
|
|
|
97
110
|
errors.push(...validateApiVersion(data.apiVersion));
|
|
98
111
|
errors.push(...validateRequiredString(data.displayName, 'displayName'));
|
|
99
112
|
errors.push(...validateRequiredString(data.description, 'description'));
|
|
100
|
-
errors.push(...
|
|
113
|
+
errors.push(...validateEnablement(data.enablement));
|
|
114
|
+
errors.push(...validatePluginIcon(data.icon));
|
|
101
115
|
errors.push(...validateOptionalString(data.frontend, 'frontend'));
|
|
102
116
|
errors.push(...validateFrontendStyles(data.frontendStyles));
|
|
103
117
|
if (data.frontendStyles !== undefined && !isNonEmptyString(data.frontend)) {
|
|
@@ -105,6 +119,9 @@ export function validateOpenForgePackageMetadata(data) {
|
|
|
105
119
|
}
|
|
106
120
|
errors.push(...validateOptionalString(data.backend, 'backend'));
|
|
107
121
|
errors.push(...validateRequires(data.requires));
|
|
122
|
+
if (data.enablement === 'app' && (!Array.isArray(data.requires) || !data.requires.includes('appEnablement'))) {
|
|
123
|
+
errors.push({ path: 'requires', message: 'App enablement requires the appEnablement capability' });
|
|
124
|
+
}
|
|
108
125
|
if (Array.isArray(data.requires) && data.requires.includes('browserSurfaces') && !isNonEmptyString(data.frontend)) {
|
|
109
126
|
errors.push({ path: 'requires', message: 'browserSurfaces capability requires a frontend entry' });
|
|
110
127
|
}
|
|
@@ -27,6 +27,18 @@
|
|
|
27
27
|
"required": ["requires"]
|
|
28
28
|
},
|
|
29
29
|
"then": { "required": ["frontend"] }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"if": {
|
|
33
|
+
"properties": { "enablement": { "const": "app" } },
|
|
34
|
+
"required": ["enablement"]
|
|
35
|
+
},
|
|
36
|
+
"then": {
|
|
37
|
+
"properties": {
|
|
38
|
+
"requires": { "contains": { "const": "appEnablement" } }
|
|
39
|
+
},
|
|
40
|
+
"required": ["requires"]
|
|
41
|
+
}
|
|
30
42
|
}
|
|
31
43
|
],
|
|
32
44
|
"properties": {
|
|
@@ -47,10 +59,33 @@
|
|
|
47
59
|
"type": "string",
|
|
48
60
|
"minLength": 1
|
|
49
61
|
},
|
|
62
|
+
"enablement": {
|
|
63
|
+
"enum": ["app", "project"],
|
|
64
|
+
"default": "project",
|
|
65
|
+
"description": "Lifecycle ownership for this plugin package. Omission means project-owned enablement."
|
|
66
|
+
},
|
|
50
67
|
"icon": {
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{
|
|
70
|
+
"type": "string",
|
|
71
|
+
"minLength": 1,
|
|
72
|
+
"pattern": "\\S"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["type", "svg"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"type": { "const": "svg" },
|
|
80
|
+
"svg": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"minLength": 1,
|
|
83
|
+
"pattern": "\\S"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"description": "Lucide icon name in kebab-case or inline SVG geometry using the PluginIcon contract."
|
|
54
89
|
},
|
|
55
90
|
"frontend": {
|
|
56
91
|
"type": "string",
|
|
@@ -101,7 +136,9 @@
|
|
|
101
136
|
"config",
|
|
102
137
|
"projectConfig",
|
|
103
138
|
"browserSurfaces",
|
|
104
|
-
"taskLinks"
|
|
139
|
+
"taskLinks",
|
|
140
|
+
"appEnablement",
|
|
141
|
+
"customSidebarNavigation"
|
|
105
142
|
]
|
|
106
143
|
}
|
|
107
144
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function isPluginIconName(value) {
|
|
2
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
3
|
+
}
|
|
4
|
+
export function isPluginSvgIcon(value) {
|
|
5
|
+
if (value === null || typeof value !== 'object')
|
|
6
|
+
return false;
|
|
7
|
+
const icon = value;
|
|
8
|
+
return Object.keys(icon).length === 2
|
|
9
|
+
&& icon.type === 'svg'
|
|
10
|
+
&& typeof icon.svg === 'string'
|
|
11
|
+
&& icon.svg.trim().length > 0;
|
|
12
|
+
}
|
package/dist/pluginIcons.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import DOMPurify from 'dompurify';
|
|
2
|
+
import { isPluginSvgIcon } from './pluginIconContract';
|
|
2
3
|
export const MAX_PLUGIN_SVG_ICON_CHARACTERS = 10_000;
|
|
3
4
|
const ALLOWED_SVG_TAGS = ['svg', 'g', 'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon'];
|
|
4
5
|
const ALLOWED_SVG_ATTRIBUTES = [
|
|
@@ -33,12 +34,6 @@ const ALLOWED_SVG_ATTRIBUTES = [
|
|
|
33
34
|
'points',
|
|
34
35
|
];
|
|
35
36
|
const SAFE_PAINT_VALUE = /^(?:none|currentColor|transparent|#[0-9a-f]{3,8}|[a-z]+|(?:rgb|rgba|hsl|hsla)\([\d\s.,%+\-/]+\))$/iu;
|
|
36
|
-
function isPluginSvgIcon(value) {
|
|
37
|
-
return value !== null
|
|
38
|
-
&& typeof value === 'object'
|
|
39
|
-
&& value.type === 'svg'
|
|
40
|
-
&& typeof value.svg === 'string';
|
|
41
|
-
}
|
|
42
37
|
function parseSvgRoot(markup) {
|
|
43
38
|
const template = document.createElement('template');
|
|
44
39
|
template.innerHTML = markup.trim();
|
package/dist/publicUiExports.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FrontendOpenForgeAPI, OpenForgeCommonAPI } from '../types';
|
|
1
|
+
import type { BackendOpenForgeAPI, FrontendOpenForgeAPI, OpenForgeCommonAPI } from '../types';
|
|
2
2
|
import { type TestingRegistryServices } from './support';
|
|
3
3
|
import type { TestingCommandContribution, TestingEventListenerContribution } from './contracts';
|
|
4
4
|
export type TestingCommonApi = OpenForgeCommonAPI & Pick<FrontendOpenForgeAPI, 'navigation'>;
|
|
@@ -10,6 +10,7 @@ export declare class TestingCommonApiFake {
|
|
|
10
10
|
private eventListenerSequence;
|
|
11
11
|
constructor(services: TestingRegistryServices);
|
|
12
12
|
createApi(): TestingCommonApi;
|
|
13
|
+
createBackendApi(): TestingCommonApi & Pick<BackendOpenForgeAPI, 'fs'>;
|
|
13
14
|
getSnapshot(): {
|
|
14
15
|
commands: TestingCommandContribution[];
|
|
15
16
|
eventListeners: TestingEventListenerContribution[];
|
|
@@ -136,7 +136,7 @@ export class TestingCommonApiFake {
|
|
|
136
136
|
},
|
|
137
137
|
getBuffer: async (request) => {
|
|
138
138
|
this.services.calls.shellBuffers.push(request);
|
|
139
|
-
return null;
|
|
139
|
+
return { buffer: null, isLive: false };
|
|
140
140
|
},
|
|
141
141
|
},
|
|
142
142
|
notifications: {
|
|
@@ -183,6 +183,38 @@ export class TestingCommonApiFake {
|
|
|
183
183
|
};
|
|
184
184
|
return api;
|
|
185
185
|
}
|
|
186
|
+
createBackendApi() {
|
|
187
|
+
const api = this.createApi();
|
|
188
|
+
return {
|
|
189
|
+
...api,
|
|
190
|
+
fs: {
|
|
191
|
+
...api.fs,
|
|
192
|
+
userData: {
|
|
193
|
+
readDir: async (request = {}) => {
|
|
194
|
+
this.services.calls.fsUserDataReadDirs.push(request);
|
|
195
|
+
return [];
|
|
196
|
+
},
|
|
197
|
+
readTextFile: async (request) => {
|
|
198
|
+
this.services.calls.fsUserDataReads.push(request);
|
|
199
|
+
return '';
|
|
200
|
+
},
|
|
201
|
+
writeTextFile: async (request) => {
|
|
202
|
+
this.services.calls.fsUserDataWrites.push(request);
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
external: {
|
|
206
|
+
readDir: async (request) => {
|
|
207
|
+
this.services.calls.fsExternalReadDirs.push(request);
|
|
208
|
+
return [];
|
|
209
|
+
},
|
|
210
|
+
readTextFile: async (request) => {
|
|
211
|
+
this.services.calls.fsExternalReads.push(request);
|
|
212
|
+
return '';
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
186
218
|
getSnapshot() {
|
|
187
219
|
return {
|
|
188
220
|
commands: Array.from(this.commands.values()),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BrowserSurfaceVisualFeedback } from '../browserSurfaces';
|
|
2
2
|
import type { TestingOpenForgeRegistryFake } from './registryFake';
|
|
3
|
-
import type { BackendMethodRegistration, BackendOpenForgeAPI, BackgroundServiceRegistration, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ConfigureStartPromptContributionRequest, CreateTaskRequest, FrontendOpenForgeAPI, InjectionPointLocation, JsonValue, NotificationRequest, OpenForgeNavigationRequest, OpenForgePackageMetadata, PluginSettingsSectionRegistration, PluginStorage, PluginTaskPaneTabRegistration, PluginTaskUISectionRegistration, PluginViewRegistration, ShellSpawnRequest, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskLinkOpenRequest, TaskStartPrefixContext } from '../types';
|
|
3
|
+
import type { BackendMethodRegistration, BackendOpenForgeAPI, BackgroundServiceRegistration, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ConfigureStartPromptContributionRequest, CreateTaskRequest, FrontendOpenForgeAPI, InjectionPointLocation, JsonValue, NotificationRequest, OpenForgeNavigationRequest, OpenForgePackageMetadata, PluginSettingsSectionRegistration, PluginStorage, PluginTaskPaneTabRegistration, PluginTaskUISectionRegistration, PluginViewRegistration, ShellSpawnRequest, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskLinkOpenRequest, TaskStartPrefixContext, ExternalReadDirectoryRequest, ExternalReadFileRequest, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileWriteRequest } from '../types';
|
|
4
4
|
import type { Task } from '../domain';
|
|
5
5
|
export type TestingRuntimeScope = 'global' | 'project' | 'task';
|
|
6
6
|
export type TestingRuntimeKind = 'commands' | 'events' | 'views' | 'taskPane' | 'taskUI' | 'settings' | 'backend' | 'background';
|
|
@@ -74,6 +74,11 @@ export interface TestingOpenForgeApiCalls {
|
|
|
74
74
|
path: string;
|
|
75
75
|
content: string;
|
|
76
76
|
}>;
|
|
77
|
+
fsUserDataReadDirs: UserDataDirectoryRequest[];
|
|
78
|
+
fsUserDataReads: UserDataFileRequest[];
|
|
79
|
+
fsUserDataWrites: UserDataFileWriteRequest[];
|
|
80
|
+
fsExternalReadDirs: ExternalReadDirectoryRequest[];
|
|
81
|
+
fsExternalReads: ExternalReadFileRequest[];
|
|
77
82
|
shellSpawns: ShellSpawnRequest[];
|
|
78
83
|
shellWrites: Array<{
|
|
79
84
|
taskId: string;
|
|
@@ -102,6 +102,15 @@ export class TestingFrontendContributionFake {
|
|
|
102
102
|
const qualifiedId = this.services.localQualifiedId('views', registration.id);
|
|
103
103
|
assertTitle('views', registration.title);
|
|
104
104
|
assertFunction('views', 'component', registration.component);
|
|
105
|
+
if (registration.navigationComponent !== undefined) {
|
|
106
|
+
assertFunction('views', 'navigationComponent', registration.navigationComponent);
|
|
107
|
+
if (registration.placement !== 'sidebar') {
|
|
108
|
+
throw new Error('views registration custom navigation requires sidebar placement');
|
|
109
|
+
}
|
|
110
|
+
if (!this.services.packageMetadata.requires?.includes('customSidebarNavigation')) {
|
|
111
|
+
throw new Error('views registration custom navigation requires the customSidebarNavigation capability');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
105
114
|
this.services.claims.claim('views', qualifiedId);
|
|
106
115
|
const contribution = {
|
|
107
116
|
...registration,
|
|
@@ -58,7 +58,7 @@ export class TestingOpenForgeRegistryFake {
|
|
|
58
58
|
if (this.cachedBackendApi)
|
|
59
59
|
return this.cachedBackendApi;
|
|
60
60
|
const api = {
|
|
61
|
-
...this.commonApi.
|
|
61
|
+
...this.commonApi.createBackendApi(),
|
|
62
62
|
...this.backendServices.createApi(),
|
|
63
63
|
__testing: {
|
|
64
64
|
calls: this.calls,
|
package/dist/testing/support.js
CHANGED
|
@@ -48,6 +48,11 @@ export function createTestingCalls() {
|
|
|
48
48
|
taskStatusUpdates: [],
|
|
49
49
|
configWrites: [],
|
|
50
50
|
fsWrites: [],
|
|
51
|
+
fsUserDataReadDirs: [],
|
|
52
|
+
fsUserDataReads: [],
|
|
53
|
+
fsUserDataWrites: [],
|
|
54
|
+
fsExternalReadDirs: [],
|
|
55
|
+
fsExternalReads: [],
|
|
51
56
|
shellSpawns: [],
|
|
52
57
|
shellWrites: [],
|
|
53
58
|
shellResizes: [],
|
package/dist/types.d.ts
CHANGED
|
@@ -17,13 +17,16 @@ export interface ValidationError {
|
|
|
17
17
|
path: string;
|
|
18
18
|
message: string;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
declare const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS: readonly ["commands", "events", "views", "injectionPoints", "taskPane", "taskStart", "settings", "background", "backend", "storage", "context", "navigation", "tasks", "projects", "fs", "shell", "notifications", "attention", "system.openUrl", "system.writeClipboardText", "config", "projectConfig", "browserSurfaces", "taskLinks", "appEnablement", "customSidebarNavigation"];
|
|
21
|
+
export type OpenForgePluginCapability = (typeof OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS)[number];
|
|
21
22
|
export interface OpenForgePackageMetadata {
|
|
22
23
|
id: string;
|
|
23
24
|
apiVersion: SupportedOpenForgeApiVersion;
|
|
24
25
|
displayName: string;
|
|
25
26
|
description: string;
|
|
26
|
-
|
|
27
|
+
/** Defaults to project-owned lifecycle when omitted. */
|
|
28
|
+
enablement?: 'app' | 'project';
|
|
29
|
+
icon?: PluginIcon;
|
|
27
30
|
frontend?: string;
|
|
28
31
|
frontendStyles?: string[];
|
|
29
32
|
backend?: string;
|
|
@@ -188,6 +191,21 @@ export interface PluginViewProps extends Record<string, unknown> {
|
|
|
188
191
|
api: FrontendOpenForgeAPI;
|
|
189
192
|
context: OpenForgeContextSnapshot;
|
|
190
193
|
}
|
|
194
|
+
export interface PluginSidebarViewIdentity {
|
|
195
|
+
pluginId: string;
|
|
196
|
+
id: string;
|
|
197
|
+
qualifiedId: string;
|
|
198
|
+
title: string;
|
|
199
|
+
icon: PluginIcon;
|
|
200
|
+
}
|
|
201
|
+
export interface PluginSidebarNavigationProps extends Record<string, unknown> {
|
|
202
|
+
api: FrontendOpenForgeAPI;
|
|
203
|
+
context: OpenForgeContextSnapshot;
|
|
204
|
+
active: boolean;
|
|
205
|
+
collapsed: boolean;
|
|
206
|
+
view: PluginSidebarViewIdentity;
|
|
207
|
+
onActivate: () => void;
|
|
208
|
+
}
|
|
191
209
|
export interface PluginTaskPaneProps extends Record<string, unknown> {
|
|
192
210
|
api: FrontendOpenForgeAPI;
|
|
193
211
|
context: OpenForgeContextSnapshot;
|
|
@@ -230,6 +248,8 @@ export interface PluginViewRegistration {
|
|
|
230
248
|
order?: number;
|
|
231
249
|
shortcut?: string;
|
|
232
250
|
component: PluginComponentLoader<PluginViewProps> | PluginComponent<PluginViewProps>;
|
|
251
|
+
/** Custom content for a sidebar-placed View's host-owned navigation slot. */
|
|
252
|
+
navigationComponent?: PluginComponentLoader<PluginSidebarNavigationProps> | PluginComponent<PluginSidebarNavigationProps>;
|
|
233
253
|
}
|
|
234
254
|
export interface PluginTaskPaneTabRegistration {
|
|
235
255
|
id: string;
|
|
@@ -353,6 +373,36 @@ export interface FileSystemAPI {
|
|
|
353
373
|
limit?: number;
|
|
354
374
|
}): Promise<string[]>;
|
|
355
375
|
}
|
|
376
|
+
export interface UserDataDirectoryRequest {
|
|
377
|
+
path?: string | null;
|
|
378
|
+
}
|
|
379
|
+
export interface UserDataFileRequest {
|
|
380
|
+
path: string;
|
|
381
|
+
}
|
|
382
|
+
export interface UserDataFileWriteRequest extends UserDataFileRequest {
|
|
383
|
+
content: string;
|
|
384
|
+
}
|
|
385
|
+
export interface ExternalReadDirectoryRequest {
|
|
386
|
+
root: string;
|
|
387
|
+
path?: string | null;
|
|
388
|
+
}
|
|
389
|
+
export interface ExternalReadFileRequest {
|
|
390
|
+
root: string;
|
|
391
|
+
path: string;
|
|
392
|
+
}
|
|
393
|
+
export interface UserDataFileSystemAPI {
|
|
394
|
+
readDir(request?: UserDataDirectoryRequest): Promise<FileEntry[]>;
|
|
395
|
+
readTextFile(request: UserDataFileRequest): Promise<string>;
|
|
396
|
+
writeTextFile(request: UserDataFileWriteRequest): Promise<void>;
|
|
397
|
+
}
|
|
398
|
+
export interface ExternalReadFileSystemAPI {
|
|
399
|
+
readDir(request: ExternalReadDirectoryRequest): Promise<FileEntry[]>;
|
|
400
|
+
readTextFile(request: ExternalReadFileRequest): Promise<string>;
|
|
401
|
+
}
|
|
402
|
+
export interface BackendFileSystemAPI extends FileSystemAPI {
|
|
403
|
+
userData: UserDataFileSystemAPI;
|
|
404
|
+
external: ExternalReadFileSystemAPI;
|
|
405
|
+
}
|
|
356
406
|
export type TerminalImageProtocol = 'iterm2';
|
|
357
407
|
export interface ShellSessionRequest {
|
|
358
408
|
taskId: string;
|
|
@@ -371,12 +421,16 @@ export interface ShellResizeRequest extends ShellSessionRequest {
|
|
|
371
421
|
cols: number;
|
|
372
422
|
rows: number;
|
|
373
423
|
}
|
|
424
|
+
export interface PtyBufferState {
|
|
425
|
+
buffer: string | null;
|
|
426
|
+
isLive: boolean;
|
|
427
|
+
}
|
|
374
428
|
export interface ShellAPI {
|
|
375
429
|
spawn(request: ShellSpawnRequest): Promise<number>;
|
|
376
430
|
write(request: ShellWriteRequest): Promise<void>;
|
|
377
431
|
resize(request: ShellResizeRequest): Promise<void>;
|
|
378
432
|
kill(request: ShellSessionRequest): Promise<void>;
|
|
379
|
-
getBuffer(request: ShellSessionRequest): Promise<
|
|
433
|
+
getBuffer(request: ShellSessionRequest): Promise<PtyBufferState>;
|
|
380
434
|
}
|
|
381
435
|
export interface CreateTaskRequest {
|
|
382
436
|
initialPrompt: string;
|
|
@@ -518,6 +572,7 @@ export interface FrontendOpenForgeAPI extends OpenForgeCommonAPI {
|
|
|
518
572
|
taskStart: FrontendTaskStartRegistry;
|
|
519
573
|
}
|
|
520
574
|
export interface BackendOpenForgeAPI extends OpenForgeCommonAPI {
|
|
575
|
+
fs: BackendFileSystemAPI;
|
|
521
576
|
backend: BackendMethodRegistry;
|
|
522
577
|
background: BackgroundServiceRegistry;
|
|
523
578
|
}
|
|
@@ -534,3 +589,4 @@ export declare function parsePluginViewKey(key: PluginViewKey): {
|
|
|
534
589
|
pluginId: string;
|
|
535
590
|
viewId: string;
|
|
536
591
|
};
|
|
592
|
+
export {};
|
package/dist/types.js
CHANGED
|
@@ -10,6 +10,45 @@ export const SUPPORTED_OPENFORGE_API_VERSIONS = Object.freeze(readSupportedOpenF
|
|
|
10
10
|
export const OPENFORGE_PLUGIN_API_VERSION = SUPPORTED_OPENFORGE_API_VERSIONS[0];
|
|
11
11
|
export const MIN_SUPPORTED_API_VERSION = Math.min(...SUPPORTED_OPENFORGE_API_VERSIONS);
|
|
12
12
|
export const MAX_SUPPORTED_API_VERSION = Math.max(...SUPPORTED_OPENFORGE_API_VERSIONS);
|
|
13
|
+
const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS = [
|
|
14
|
+
'commands',
|
|
15
|
+
'events',
|
|
16
|
+
'views',
|
|
17
|
+
'injectionPoints',
|
|
18
|
+
'taskPane',
|
|
19
|
+
'taskStart',
|
|
20
|
+
'settings',
|
|
21
|
+
'background',
|
|
22
|
+
'backend',
|
|
23
|
+
'storage',
|
|
24
|
+
'context',
|
|
25
|
+
'navigation',
|
|
26
|
+
'tasks',
|
|
27
|
+
'projects',
|
|
28
|
+
'fs',
|
|
29
|
+
'shell',
|
|
30
|
+
'notifications',
|
|
31
|
+
'attention',
|
|
32
|
+
'system.openUrl',
|
|
33
|
+
'system.writeClipboardText',
|
|
34
|
+
'config',
|
|
35
|
+
'projectConfig',
|
|
36
|
+
'browserSurfaces',
|
|
37
|
+
'taskLinks',
|
|
38
|
+
'appEnablement',
|
|
39
|
+
'customSidebarNavigation',
|
|
40
|
+
];
|
|
41
|
+
function assertOpenForgePluginCapabilitiesMatchSchema() {
|
|
42
|
+
const schemaCapabilities = packageMetadataSchemaData.properties.requires.items.enum;
|
|
43
|
+
if (!Array.isArray(schemaCapabilities) || !schemaCapabilities.every((capability) => typeof capability === 'string')) {
|
|
44
|
+
throw new Error('openforgePackageMetadataSchema.json properties.requires.items.enum must contain only strings');
|
|
45
|
+
}
|
|
46
|
+
if (schemaCapabilities.length !== OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS.length
|
|
47
|
+
|| schemaCapabilities.some((capability, index) => capability !== OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS[index])) {
|
|
48
|
+
throw new Error('OpenForgePluginCapability must match openforgePackageMetadataSchema.json properties.requires.items.enum');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
assertOpenForgePluginCapabilitiesMatchSchema();
|
|
13
52
|
export class TaskFollowUpError extends Error {
|
|
14
53
|
code;
|
|
15
54
|
constructor(code, message) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
accessibleName: string
|
|
6
|
+
active: boolean
|
|
7
|
+
collapsed: boolean
|
|
8
|
+
onActivate: () => void
|
|
9
|
+
leading?: Snippet
|
|
10
|
+
label?: Snippet
|
|
11
|
+
trailing?: Snippet
|
|
12
|
+
class?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
accessibleName,
|
|
17
|
+
active,
|
|
18
|
+
collapsed,
|
|
19
|
+
onActivate,
|
|
20
|
+
leading,
|
|
21
|
+
label,
|
|
22
|
+
trailing,
|
|
23
|
+
class: className,
|
|
24
|
+
}: Props = $props()
|
|
25
|
+
|
|
26
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
27
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
28
|
+
event.preventDefault()
|
|
29
|
+
onActivate()
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
class={[
|
|
36
|
+
'relative mx-2 flex min-h-11 w-auto items-center rounded-lg gap-3 py-2.5 cursor-pointer transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',
|
|
37
|
+
collapsed ? 'justify-center px-0' : 'px-3',
|
|
38
|
+
active ? 'bg-primary/10 text-primary' : 'text-base-content/55 hover:bg-base-200 hover:text-base-content',
|
|
39
|
+
className,
|
|
40
|
+
]}
|
|
41
|
+
title={collapsed ? accessibleName : undefined}
|
|
42
|
+
aria-label={accessibleName}
|
|
43
|
+
aria-current={active ? 'page' : undefined}
|
|
44
|
+
onclick={onActivate}
|
|
45
|
+
onkeydown={handleKeydown}
|
|
46
|
+
>
|
|
47
|
+
{#if leading}
|
|
48
|
+
<span class="relative shrink-0">{@render leading()}</span>
|
|
49
|
+
{/if}
|
|
50
|
+
{#if !collapsed}
|
|
51
|
+
{#if label}<span class="min-w-0 text-sm font-medium">{@render label()}</span>{/if}
|
|
52
|
+
{#if trailing}<span class="ml-auto shrink-0">{@render trailing()}</span>{/if}
|
|
53
|
+
{/if}
|
|
54
|
+
</button>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openforge-app/plugin-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"./ui/Modal.svelte": "./dist/ui/Modal.svelte",
|
|
72
72
|
"./ui/PluginPageHeader.svelte": "./dist/ui/PluginPageHeader.svelte",
|
|
73
73
|
"./ui/PluginViewState.svelte": "./dist/ui/PluginViewState.svelte",
|
|
74
|
+
"./ui/PluginSidebarLink.svelte": "./dist/ui/PluginSidebarLink.svelte",
|
|
74
75
|
"./ui/FileTypeIcon.svelte": "./dist/ui/FileTypeIcon.svelte"
|
|
75
76
|
},
|
|
76
77
|
"files": [
|