Files
makelore/shared/coding-plugins.ts

458 lines
12 KiB
TypeScript

/**
* The small, code-owned vocabulary shared by the Main plugin registry and the
* renderer-facing product contracts. Package manifests are untrusted input;
* the values below are the only first-party adapter, surface, permission and
* Data Service identifiers that the P0 registry accepts.
*/
export type PluginBillingMode =
| 'included'
| 'platform_metered'
| 'external_account';
export type PluginToolMutation = 'read' | 'write' | 'destructive';
export type CodingPluginRuntimeKind =
| 'bundled_typed'
| 'skill_only'
| 'platform_hosted';
export type CodingPluginAcquisitionMode =
| 'system_included'
| 'user_acquired';
export type CodingPluginExecutionMode = 'synchronous' | 'job';
export interface CodingPluginPackageProvenance {
readonly source: 'bundled' | 'marketplace';
/** A trusted Package Store root, never a manifest-provided value. */
readonly packageRoot: string;
}
export interface CodingPluginSkillDefinition {
id: string;
entryPath: string;
grants: readonly string[];
}
export interface CodingPluginToolDefinition {
name: string;
label: string;
description: string;
capabilityId: string;
operation: string;
roles: readonly ['parent'];
mutation: PluginToolMutation;
projectWriteLease: boolean;
permissions: readonly string[];
inputSchema: Readonly<Record<string, unknown>>;
outputSchema?: Readonly<Record<string, unknown>>;
executionMode?: CodingPluginExecutionMode;
}
export interface CodingPluginOperationDefinition {
capabilityId: string;
operation: string;
toolName?: string;
}
export interface CodingPluginDefinition {
id: string;
version: string;
contractVersion: number;
displayName: string;
description: string;
runtimeKind: CodingPluginRuntimeKind;
acquisitionMode: CodingPluginAcquisitionMode;
releaseId: string | null;
provenance: CodingPluginPackageProvenance;
scope: 'project';
adapterId: string;
requiresBackend: boolean;
skills: readonly CodingPluginSkillDefinition[];
tools: readonly CodingPluginToolDefinition[];
operations: readonly CodingPluginOperationDefinition[];
surfaces: Readonly<{
projectSettings?: string;
previewRuntime?: string;
}>;
}
export interface AgentPluginsAuthorManifest {
name: string;
}
export interface AgentPluginsRootManifest {
$schema: string;
name: string;
version: string;
description: string;
author: AgentPluginsAuthorManifest;
extensions: Readonly<{
'com.makelore': Readonly<{ capabilityManifest: string }>;
}>;
}
export const AGENT_PLUGINS_SCHEMA_URL =
'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json' as const;
export const DATA_SERVICE_PLUGIN_ID = 'makelore.data-service' as const;
export const DATA_SERVICE_ADAPTER_ID = 'data-service' as const;
export const DATA_SERVICE_PROJECT_SETTINGS_SURFACE = 'data-service' as const;
export const DATA_SERVICE_PREVIEW_RUNTIME_SURFACE = 'data-service-v1' as const;
export const DATA_SERVICE_CAPABILITY_IDS = Object.freeze([
'data-service.control',
'data-service.documents',
'data-service.preview',
] as const);
export const DATA_SERVICE_TOOL_NAMES = Object.freeze([
'data_service_configure',
'data_service_inspect',
'data_service_list_projects',
'data_service_get_document',
'data_service_list_documents',
'data_service_put_document',
'data_service_delete_document',
'data_service_remove_collection',
'data_service_reset',
'data_service_remove_project',
] as const);
export const CODE_OWNED_PLUGIN_ADAPTER_IDS = Object.freeze([
DATA_SERVICE_ADAPTER_ID,
] as const);
export const CODE_OWNED_PLUGIN_SETTINGS_SURFACES = Object.freeze([
DATA_SERVICE_PROJECT_SETTINGS_SURFACE,
] as const);
export const CODE_OWNED_PLUGIN_PREVIEW_SURFACES = Object.freeze([
DATA_SERVICE_PREVIEW_RUNTIME_SURFACE,
] as const);
export const CODE_OWNED_PLUGIN_PERMISSION_IDS = Object.freeze([
'project.data.admin',
'project.data.configure',
'project.data.read',
'project.data.write',
] as const);
// Descriptive aliases keep the allowlist vocabulary discoverable to callers
// without creating a second source of truth.
export const BUNDLED_CODING_PLUGIN_ADAPTER_IDS = CODE_OWNED_PLUGIN_ADAPTER_IDS;
export const BUNDLED_CODING_PLUGIN_SETTINGS_SURFACES = CODE_OWNED_PLUGIN_SETTINGS_SURFACES;
export const BUNDLED_CODING_PLUGIN_PREVIEW_SURFACES = CODE_OWNED_PLUGIN_PREVIEW_SURFACES;
export const BUNDLED_CODING_PLUGIN_PERMISSION_IDS = CODE_OWNED_PLUGIN_PERMISSION_IDS;
const EMPTY_OBJECT_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
properties: {},
} as const);
const COLLECTION_SCHEMA = Object.freeze({
type: 'string',
pattern: '^[a-z][a-z0-9_-]{0,47}$',
} as const);
const DOCUMENT_ID_SCHEMA = Object.freeze({
type: 'string',
pattern: '^[A-Za-z0-9._~-]{1,128}$',
} as const);
const REVISION_SCHEMA = Object.freeze({
type: 'integer',
minimum: 1,
} as const);
const DATA_SCHEMA = Object.freeze({
type: 'object',
} as const);
const LIST_LIMIT_SCHEMA = Object.freeze({
type: 'integer',
minimum: 1,
maximum: 100,
} as const);
const CURSOR_SCHEMA = Object.freeze({
type: 'string',
minLength: 1,
maxLength: 1024,
} as const);
const CONFIRMED_SCHEMA = Object.freeze({
type: 'boolean',
const: true,
} as const);
const CONFIGURE_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collections'],
properties: {
collections: {
type: 'array',
maxItems: 20,
items: COLLECTION_SCHEMA,
},
},
} as const);
const GET_DOCUMENT_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collection', 'document_id'],
properties: {
collection: COLLECTION_SCHEMA,
document_id: DOCUMENT_ID_SCHEMA,
},
} as const);
const LIST_DOCUMENTS_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collection'],
properties: {
collection: COLLECTION_SCHEMA,
limit: LIST_LIMIT_SCHEMA,
cursor: CURSOR_SCHEMA,
},
} as const);
const PUT_DOCUMENT_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collection', 'document_id', 'data'],
properties: {
collection: COLLECTION_SCHEMA,
document_id: DOCUMENT_ID_SCHEMA,
data: DATA_SCHEMA,
if_revision: REVISION_SCHEMA,
},
} as const);
const DELETE_DOCUMENT_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collection', 'document_id', 'confirmed'],
properties: {
collection: COLLECTION_SCHEMA,
document_id: DOCUMENT_ID_SCHEMA,
if_revision: REVISION_SCHEMA,
confirmed: CONFIRMED_SCHEMA,
},
} as const);
const REMOVE_COLLECTION_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['collection', 'confirmed'],
properties: {
collection: COLLECTION_SCHEMA,
confirmed: CONFIRMED_SCHEMA,
},
} as const);
const CONFIRMATION_SCHEMA = Object.freeze({
type: 'object',
additionalProperties: false,
required: ['confirmed'],
properties: { confirmed: CONFIRMED_SCHEMA },
} as const);
function freezeDeep<T>(value: T): T {
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
for (const child of Object.values(value as Record<string, unknown>)) freezeDeep(child);
Object.freeze(value);
}
return value;
}
function tool(
name: (typeof DATA_SERVICE_TOOL_NAMES)[number],
label: string,
description: string,
capabilityId: string,
operation: string,
mutation: PluginToolMutation,
projectWriteLease: boolean,
permissions: readonly string[],
inputSchema: Readonly<Record<string, unknown>>,
): CodingPluginToolDefinition {
return {
name,
label,
description,
capabilityId,
operation,
roles: ['parent'],
mutation,
projectWriteLease,
permissions,
inputSchema,
};
}
export const DATA_SERVICE_TOOL_DEFINITIONS = freezeDeep([
tool(
'data_service_configure',
'Data Service configure',
'Configure collections for the active project.',
'data-service.control',
'configure',
'write',
true,
['project.data.configure'],
CONFIGURE_SCHEMA,
),
tool(
'data_service_inspect',
'Data Service inspect',
'Inspect the active project data instance.',
'data-service.control',
'inspect',
'read',
false,
['project.data.read'],
EMPTY_OBJECT_SCHEMA,
),
tool(
'data_service_list_projects',
'Data Service list projects',
'List the authenticated projects with Data Service instances.',
'data-service.control',
'list_projects',
'read',
false,
['project.data.read'],
EMPTY_OBJECT_SCHEMA,
),
tool(
'data_service_get_document',
'Data Service get document',
'Read a document from the active project.',
'data-service.documents',
'get_document',
'read',
false,
['project.data.read'],
GET_DOCUMENT_SCHEMA,
),
tool(
'data_service_list_documents',
'Data Service list documents',
'List documents from a collection in the active project.',
'data-service.documents',
'list_documents',
'read',
false,
['project.data.read'],
LIST_DOCUMENTS_SCHEMA,
),
tool(
'data_service_put_document',
'Data Service put document',
'Create or replace a document in the active project.',
'data-service.documents',
'put_document',
'write',
true,
['project.data.write'],
PUT_DOCUMENT_SCHEMA,
),
tool(
'data_service_delete_document',
'Data Service delete document',
'Delete a document after explicit confirmation.',
'data-service.documents',
'delete_document',
'destructive',
true,
['project.data.write'],
DELETE_DOCUMENT_SCHEMA,
),
tool(
'data_service_remove_collection',
'Data Service remove collection',
'Remove a collection after explicit confirmation.',
'data-service.control',
'remove_collection',
'destructive',
true,
['project.data.admin'],
REMOVE_COLLECTION_SCHEMA,
),
tool(
'data_service_reset',
'Data Service reset',
'Reset all active project data after explicit confirmation.',
'data-service.control',
'reset',
'destructive',
true,
['project.data.admin'],
CONFIRMATION_SCHEMA,
),
tool(
'data_service_remove_project',
'Data Service remove project',
'Remove the active project data instance after explicit confirmation.',
'data-service.control',
'remove_project',
'destructive',
true,
['project.data.admin'],
CONFIRMATION_SCHEMA,
),
] as const satisfies readonly CodingPluginToolDefinition[]);
export const DATA_SERVICE_OPERATION_DEFINITIONS = freezeDeep([
...DATA_SERVICE_TOOL_DEFINITIONS.map(({ capabilityId, operation, name }) => ({
capabilityId,
operation,
toolName: name,
})),
...['get', 'list', 'put', 'delete'].map((operation) => ({
capabilityId: 'data-service.preview',
operation,
})),
] as const satisfies readonly CodingPluginOperationDefinition[]);
export const DATA_SERVICE_PLUGIN_DEFINITION = freezeDeep({
id: DATA_SERVICE_PLUGIN_ID,
version: '1.0.0',
contractVersion: 1,
displayName: '开发数据服务',
description: '为当前项目提供受控的开发期 JSON 数据存储。',
runtimeKind: 'bundled_typed',
acquisitionMode: 'system_included',
releaseId: null,
provenance: {
source: 'bundled',
packageRoot: 'data-service',
},
scope: 'project',
adapterId: DATA_SERVICE_ADAPTER_ID,
requiresBackend: true,
skills: [
{
id: 'data-service',
entryPath: 'skills/data-service/SKILL.md',
grants: ['data-service.control', 'data-service.documents'],
},
],
tools: DATA_SERVICE_TOOL_DEFINITIONS,
operations: DATA_SERVICE_OPERATION_DEFINITIONS,
surfaces: {
projectSettings: DATA_SERVICE_PROJECT_SETTINGS_SURFACE,
previewRuntime: DATA_SERVICE_PREVIEW_RUNTIME_SURFACE,
},
} as const satisfies CodingPluginDefinition);
export const BUNDLED_CODING_PLUGIN_DEFINITIONS = freezeDeep([
DATA_SERVICE_PLUGIN_DEFINITION,
] as const satisfies readonly CodingPluginDefinition[]);