feat: add manifest schema, TypeScript types, PluginAPI, RPC client, event schemas, sync schemas
This commit is contained in:
parent
622fcf6625
commit
2f02db00f5
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "@verstak/plugin-sdk",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Verstak Plugin SDK — TypeScript types and runtime API for plugin development",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist/",
|
||||||
|
"schemas/"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "tsc --noEmit",
|
||||||
|
"prepack": "npm run build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.4.0",
|
||||||
|
"vitest": "^1.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/capabilities.json",
|
||||||
|
"title": "Verstak Capability Registry",
|
||||||
|
"description": "Known capability names and their descriptions for the Verstak platform",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"capabilities": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/CapabilityEntry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"CapabilityEntry": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["stable", "draft", "deprecated"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "status"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"capabilities": [
|
||||||
|
{ "name": "editor.text", "description": "Text editing capability (any format)", "status": "draft" },
|
||||||
|
{ "name": "editor.text.markdown", "description": "Markdown text editing", "status": "draft" },
|
||||||
|
{ "name": "editor.note.markdown", "description": "Markdown note editing (extends editor.text.markdown with note metadata)", "status": "draft" },
|
||||||
|
{ "name": "viewer.file", "description": "File viewer (any type)", "status": "draft" },
|
||||||
|
{ "name": "viewer.image", "description": "Image viewer", "status": "draft" },
|
||||||
|
{ "name": "viewer.text", "description": "Text file viewer", "status": "draft" },
|
||||||
|
{ "name": "viewer.markdown", "description": "Markdown rendered viewer", "status": "draft" },
|
||||||
|
{ "name": "preview.markdown", "description": "Markdown preview panel", "status": "draft" },
|
||||||
|
{ "name": "preview.file", "description": "File preview panel", "status": "draft" },
|
||||||
|
{ "name": "workspace.files", "description": "File workspace management", "status": "draft" },
|
||||||
|
{ "name": "workspace.notes", "description": "Note workspace management", "status": "draft" },
|
||||||
|
{ "name": "vault.files", "description": "Low-level vault file read/write access", "status": "draft" },
|
||||||
|
{ "name": "entity.file", "description": "File entity type support", "status": "draft" },
|
||||||
|
{ "name": "entity.note", "description": "Note entity type support", "status": "draft" },
|
||||||
|
{ "name": "note.registry", "description": "Note metadata registry", "status": "draft" },
|
||||||
|
{ "name": "file.browser", "description": "File browser tree/list UI", "status": "draft" },
|
||||||
|
{ "name": "activity.log", "description": "Activity event logging", "status": "draft" },
|
||||||
|
{ "name": "activity.provider", "description": "Activity event provider", "status": "draft" },
|
||||||
|
{ "name": "activity.reconstruction", "description": "Activity reconstruction from events", "status": "draft" },
|
||||||
|
{ "name": "worklog", "description": "Worklog/journal entry capability", "status": "draft" },
|
||||||
|
{ "name": "journal", "description": "Journal UI capability", "status": "draft" },
|
||||||
|
{ "name": "report.worklog", "description": "Worklog report generation", "status": "draft" },
|
||||||
|
{ "name": "capture.browser", "description": "Browser capture receiver", "status": "draft" },
|
||||||
|
{ "name": "browser.inbox", "description": "Browser inbox UI capability", "status": "draft" },
|
||||||
|
{ "name": "domain.binding", "description": "Domain-to-case binding", "status": "draft" },
|
||||||
|
{ "name": "search", "description": "Full-text search capability", "status": "draft" },
|
||||||
|
{ "name": "search.provider", "description": "Search result provider", "status": "draft" },
|
||||||
|
{ "name": "search.indexer", "description": "Search indexer service", "status": "draft" },
|
||||||
|
{ "name": "secret-store", "description": "Encrypted secret storage", "status": "draft" },
|
||||||
|
{ "name": "secrets.read-ui", "description": "Secret read user interface", "status": "draft" },
|
||||||
|
{ "name": "secrets.write-ui", "description": "Secret write user interface", "status": "draft" },
|
||||||
|
{ "name": "case.templates", "description": "Case template provider", "status": "draft" },
|
||||||
|
{ "name": "link.resolver", "description": "Internal link resolver (verstak://)", "status": "draft" },
|
||||||
|
{ "name": "importer", "description": "Data import capability", "status": "draft" },
|
||||||
|
{ "name": "exporter", "description": "Data export capability", "status": "draft" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/contributions.json",
|
||||||
|
"title": "Verstak Contribution Points Registry",
|
||||||
|
"description": "Known contribution points that plugins can register",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"contributionPoints": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionPoint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"ContributionPoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["view", "command", "item", "provider", "panel", "action", "entry"]
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["stable", "draft", "deprecated"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "description", "type", "status"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contributionPoints": [
|
||||||
|
{ "id": "sidebar.items", "description": "Sidebar navigation items", "type": "item", "status": "draft" },
|
||||||
|
{ "id": "main.views", "description": "Main content views", "type": "view", "status": "draft" },
|
||||||
|
{ "id": "case.tabs", "description": "Case detail tabs", "type": "view", "status": "draft" },
|
||||||
|
{ "id": "file.actions", "description": "File context actions", "type": "action", "status": "draft" },
|
||||||
|
{ "id": "note.actions", "description": "Note context actions", "type": "action", "status": "draft" },
|
||||||
|
{ "id": "context.menu", "description": "Context menu entries", "type": "entry", "status": "draft" },
|
||||||
|
{ "id": "commands", "description": "Command palette commands", "type": "command", "status": "draft" },
|
||||||
|
{ "id": "settings.panels", "description": "Plugin settings panels", "type": "panel", "status": "draft" },
|
||||||
|
{ "id": "search.providers", "description": "Search result providers", "type": "provider", "status": "draft" },
|
||||||
|
{ "id": "activity.providers", "description": "Activity event providers", "type": "provider", "status": "draft" },
|
||||||
|
{ "id": "status.bar.items", "description": "Status bar items", "type": "item", "status": "draft" },
|
||||||
|
{ "id": "importers", "description": "Data import providers", "type": "provider", "status": "draft" },
|
||||||
|
{ "id": "exporters", "description": "Data export providers", "type": "provider", "status": "draft" },
|
||||||
|
{ "id": "protocol.receivers", "description": "Custom protocol message receivers", "type": "provider", "status": "draft" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/events/browser.json",
|
||||||
|
"title": "Verstak Browser Events",
|
||||||
|
"description": "Event schemas for browser extension and browser-inbox plugin communication",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"events": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/BrowserEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"BrowserEvent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string", "const": "object" },
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{ "type": "string" },
|
||||||
|
{ "type": "integer" },
|
||||||
|
{ "type": "boolean" },
|
||||||
|
{ "type": "array" },
|
||||||
|
{ "type": "object" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "properties", "required"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "schema"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "browser.capture.page",
|
||||||
|
"description": "Full page capture sent from browser extension",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": { "type": "string", "description": "Page URL" },
|
||||||
|
"title": { "type": "string", "description": "Page title" },
|
||||||
|
"html": { "type": "string", "description": "Page HTML content" },
|
||||||
|
"text": { "type": "string", "description": "Page text content" },
|
||||||
|
"capturedAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"domain": { "type": "string", "description": "Extracted domain" }
|
||||||
|
},
|
||||||
|
"required": ["url", "title", "capturedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "browser.capture.selection",
|
||||||
|
"description": "Selected text sent from browser extension",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": { "type": "string", "description": "Source page URL" },
|
||||||
|
"title": { "type": "string", "description": "Source page title" },
|
||||||
|
"text": { "type": "string", "description": "Selected text content" },
|
||||||
|
"capturedAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"domain": { "type": "string", "description": "Extracted domain" }
|
||||||
|
},
|
||||||
|
"required": ["url", "title", "text", "capturedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "browser.capture.link",
|
||||||
|
"description": "Link sent from browser extension",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": { "type": "string", "description": "Link URL" },
|
||||||
|
"title": { "type": "string", "description": "Link text/title" },
|
||||||
|
"capturedAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"domain": { "type": "string", "description": "Extracted domain" }
|
||||||
|
},
|
||||||
|
"required": ["url", "capturedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "browser.desktop.status",
|
||||||
|
"description": "Desktop status response to browser extension ping",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"online": { "type": "boolean", "description": "Whether desktop is reachable" },
|
||||||
|
"version": { "type": "string", "description": "Desktop version" },
|
||||||
|
"checkedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["online", "checkedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "browser.pending.flush",
|
||||||
|
"description": "Flush pending captures from browser queue to desktop",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string", "enum": ["page", "selection", "link"] },
|
||||||
|
"payload": { "type": "object" },
|
||||||
|
"queuedAt": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["type", "payload"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flushedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["items", "flushedAt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/events/lifecycle.json",
|
||||||
|
"title": "Verstak Lifecycle Events",
|
||||||
|
"description": "Event schemas for plugin and application lifecycle events",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"events": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/LifecycleEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"LifecycleEvent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string", "const": "object" },
|
||||||
|
"properties": { "type": "object" },
|
||||||
|
"required": { "type": "array", "items": { "type": "string" } }
|
||||||
|
},
|
||||||
|
"required": ["type", "properties", "required"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "schema"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "plugin.enabled",
|
||||||
|
"description": "A plugin has been enabled",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pluginId": { "type": "string", "description": "Plugin identifier" },
|
||||||
|
"version": { "type": "string", "description": "Plugin version" },
|
||||||
|
"enabledAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["pluginId", "enabledAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plugin.disabled",
|
||||||
|
"description": "A plugin has been disabled",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pluginId": { "type": "string", "description": "Plugin identifier" },
|
||||||
|
"disabledAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["pluginId", "disabledAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plugin.failed",
|
||||||
|
"description": "A plugin has failed to load or run",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pluginId": { "type": "string", "description": "Plugin identifier" },
|
||||||
|
"error": { "type": "string", "description": "Error message" },
|
||||||
|
"phase": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Lifecycle phase where failure occurred",
|
||||||
|
"enum": ["discovery", "validation", "dependency", "permissions", "sidecar", "frontend", "registration", "runtime"]
|
||||||
|
},
|
||||||
|
"failedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["pluginId", "error", "phase", "failedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plugin.installed",
|
||||||
|
"description": "A new plugin has been installed",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pluginId": { "type": "string" },
|
||||||
|
"version": { "type": "string" },
|
||||||
|
"source": { "type": "string", "enum": ["official", "local", "third-party"] },
|
||||||
|
"installedAt": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["pluginId", "version", "source", "installedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "plugin.uninstalled",
|
||||||
|
"description": "A plugin has been uninstalled",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pluginId": { "type": "string" },
|
||||||
|
"preserveData": { "type": "boolean", "description": "Whether user chose to keep data" },
|
||||||
|
"uninstalledAt": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["pluginId", "uninstalledAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "app.started",
|
||||||
|
"description": "Application has started",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"version": { "type": "string", "description": "Application version" },
|
||||||
|
"startedAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"pluginsLoaded": { "type": "integer", "description": "Number of plugins loaded" },
|
||||||
|
"pluginsFailed": { "type": "integer", "description": "Number of plugins failed" }
|
||||||
|
},
|
||||||
|
"required": ["version", "startedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "app.shuttingdown",
|
||||||
|
"description": "Application is shutting down",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"shutdownAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"reason": { "type": "string", "description": "Shutdown reason" }
|
||||||
|
},
|
||||||
|
"required": ["shutdownAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "capability.changed",
|
||||||
|
"description": "Available capabilities have changed (plugin enabled/disabled)",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"added": { "type": "array", "items": { "type": "string" }, "description": "Capabilities that became available" },
|
||||||
|
"removed": { "type": "array", "items": { "type": "string" }, "description": "Capabilities that were removed" },
|
||||||
|
"changedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["changedAt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/events/vault.json",
|
||||||
|
"title": "Verstak Vault Events",
|
||||||
|
"description": "Event schemas for vault and case lifecycle events",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"events": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/VaultEvent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"VaultEvent": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string", "const": "object" },
|
||||||
|
"properties": { "type": "object" },
|
||||||
|
"required": { "type": "array", "items": { "type": "string" } }
|
||||||
|
},
|
||||||
|
"required": ["type", "properties", "required"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "schema"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "vault.opened",
|
||||||
|
"description": "Vault has been opened and is ready",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": { "type": "string", "description": "Absolute path to vault root" },
|
||||||
|
"version": { "type": "string", "description": "Vault schema version" },
|
||||||
|
"openedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["path", "openedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vault.closed",
|
||||||
|
"description": "Vault is about to close",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"closedAt": { "type": "string", "description": "ISO 8601 timestamp" },
|
||||||
|
"clean": { "type": "boolean", "description": "Whether vault closed cleanly" }
|
||||||
|
},
|
||||||
|
"required": ["closedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "case.selected",
|
||||||
|
"description": "A case has been selected in the navigation",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"caseId": { "type": "string", "description": "Case identifier" },
|
||||||
|
"casePath": { "type": "string", "description": "Case filesystem path" },
|
||||||
|
"caseType": { "type": "string", "description": "Case type (client, project, etc.)" },
|
||||||
|
"selectedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["caseId", "casePath"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "file.added",
|
||||||
|
"description": "A file has been added to the vault",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": { "type": "string", "description": "File path relative to vault" },
|
||||||
|
"size": { "type": "integer", "description": "File size in bytes" },
|
||||||
|
"mimeType": { "type": "string", "description": "File MIME type" },
|
||||||
|
"caseId": { "type": "string", "description": "Associated case identifier" },
|
||||||
|
"addedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["path", "addedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "file.changed",
|
||||||
|
"description": "A file in the vault has been modified",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": { "type": "string", "description": "File path relative to vault" },
|
||||||
|
"size": { "type": "integer", "description": "New file size" },
|
||||||
|
"changedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["path", "changedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "file.deleted",
|
||||||
|
"description": "A file has been deleted from the vault",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": { "type": "string", "description": "File path relative to vault" },
|
||||||
|
"deletedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["path", "deletedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "note.saved",
|
||||||
|
"description": "A note has been saved",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"noteId": { "type": "string", "description": "Note identifier" },
|
||||||
|
"title": { "type": "string", "description": "Note title" },
|
||||||
|
"path": { "type": "string", "description": "Note file path relative to vault" },
|
||||||
|
"caseId": { "type": "string", "description": "Associated case identifier" },
|
||||||
|
"savedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["noteId", "path", "savedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "activity.recorded",
|
||||||
|
"description": "An activity event has been recorded",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "type": "string", "description": "Activity type (file.opened, note.saved, etc.)" },
|
||||||
|
"caseId": { "type": "string", "description": "Associated case" },
|
||||||
|
"details": { "type": "object", "description": "Activity-specific details" },
|
||||||
|
"recordedAt": { "type": "string", "description": "ISO 8601 timestamp" }
|
||||||
|
},
|
||||||
|
"required": ["type", "recordedAt"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "case.created",
|
||||||
|
"description": "A new case has been created",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"caseId": { "type": "string" },
|
||||||
|
"casePath": { "type": "string" },
|
||||||
|
"caseType": { "type": "string" },
|
||||||
|
"template": { "type": "string", "description": "Template used, if any" },
|
||||||
|
"createdAt": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["caseId", "casePath", "createdAt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/manifest.json",
|
||||||
|
"title": "Verstak Plugin Manifest",
|
||||||
|
"description": "Schema for Verstak plugin.json manifest files",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"schemaVersion",
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"apiVersion",
|
||||||
|
"provides",
|
||||||
|
"permissions"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schemaVersion": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Schema version for the manifest format",
|
||||||
|
"enum": [1]
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unique plugin identifier (e.g. 'official.notes')",
|
||||||
|
"pattern": "^[a-zA-Z][a-zA-Z0-9.-]*$"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Human-readable plugin name"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Plugin semantic version",
|
||||||
|
"pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?$"
|
||||||
|
},
|
||||||
|
"apiVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Verstak Platform API version this plugin targets",
|
||||||
|
"pattern": "^\\d+$"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Brief description of the plugin's purpose"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Plugin source classification",
|
||||||
|
"enum": ["official", "local", "third-party"]
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to plugin icon (relative to plugin root)"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Capabilities this plugin provides",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"minItems": 1
|
||||||
|
},
|
||||||
|
"requires": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Required capabilities — plugin cannot load without these",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"optionalRequires": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Optional capabilities — plugin degrades gracefully without these",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Runtime permissions requested by the plugin",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"vault.read",
|
||||||
|
"vault.write",
|
||||||
|
"vault.watch",
|
||||||
|
"storage.namespace",
|
||||||
|
"storage.migrations",
|
||||||
|
"events.publish",
|
||||||
|
"events.subscribe",
|
||||||
|
"ui.register",
|
||||||
|
"commands.register",
|
||||||
|
"network.local",
|
||||||
|
"network.remote",
|
||||||
|
"process.spawn",
|
||||||
|
"secrets.read",
|
||||||
|
"secrets.write",
|
||||||
|
"sync.participate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minItems": 1
|
||||||
|
},
|
||||||
|
"frontend": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Frontend bundle configuration",
|
||||||
|
"properties": {
|
||||||
|
"entry": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the frontend entry JavaScript file"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the frontend stylesheet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["entry"]
|
||||||
|
},
|
||||||
|
"backend": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Backend sidecar configuration",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["sidecar"],
|
||||||
|
"description": "Backend type (only 'sidecar' supported)"
|
||||||
|
},
|
||||||
|
"entry": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Platform-specific binaries",
|
||||||
|
"patternProperties": {
|
||||||
|
"^(linux|windows|darwin)-(amd64|arm64)$": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to the binary for this platform"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minProperties": 1
|
||||||
|
},
|
||||||
|
"healthCheck": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Sidecar health check configuration",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rpc", "stdio", "tcp"],
|
||||||
|
"default": "rpc"
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Health check timeout in milliseconds",
|
||||||
|
"default": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "entry"]
|
||||||
|
},
|
||||||
|
"migrations": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Database migration configuration",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to migrations directory"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contributes": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "UI and action contributions",
|
||||||
|
"properties": {
|
||||||
|
"views": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionView"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionCommand"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settingsPanels": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionSettingsPanel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sidebarItems": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionSidebarItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fileActions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionAction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"noteActions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionAction"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contextMenuEntries": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionContextMenuEntry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"searchProviders": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionSearchProvider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"activityProviders": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionActivityProvider"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statusBarItems": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ContributionStatusBarItem"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Sync configuration — what data this plugin allows syncing",
|
||||||
|
"properties": {
|
||||||
|
"namespaces": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Storage namespaces allowed for sync",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"participate": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether this plugin participates in sync at all",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"ContributionView": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"title": { "type": "string" },
|
||||||
|
"icon": { "type": "string" },
|
||||||
|
"component": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "component"]
|
||||||
|
},
|
||||||
|
"ContributionCommand": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"title": { "type": "string" },
|
||||||
|
"keybinding": { "type": "string" },
|
||||||
|
"icon": { "type": "string" },
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "title"]
|
||||||
|
},
|
||||||
|
"ContributionSettingsPanel": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"title": { "type": "string" },
|
||||||
|
"component": { "type": "string" },
|
||||||
|
"icon": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "title", "component"]
|
||||||
|
},
|
||||||
|
"ContributionSidebarItem": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"title": { "type": "string" },
|
||||||
|
"icon": { "type": "string" },
|
||||||
|
"view": { "type": "string" },
|
||||||
|
"position": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Sort order (lower = higher)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "title", "view"]
|
||||||
|
},
|
||||||
|
"ContributionAction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"label": { "type": "string" },
|
||||||
|
"icon": { "type": "string" },
|
||||||
|
"capability": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Required capability for this action to appear"
|
||||||
|
},
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
},
|
||||||
|
"ContributionContextMenuEntry": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"label": { "type": "string" },
|
||||||
|
"context": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["file", "note", "case", "folder"]
|
||||||
|
},
|
||||||
|
"group": { "type": "string" },
|
||||||
|
"capability": { "type": "string" },
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "label", "context"]
|
||||||
|
},
|
||||||
|
"ContributionSearchProvider": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"label": { "type": "string" },
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "label", "handler"]
|
||||||
|
},
|
||||||
|
"ContributionActivityProvider": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"events": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "handler"]
|
||||||
|
},
|
||||||
|
"ContributionStatusBarItem": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string" },
|
||||||
|
"label": { "type": "string" },
|
||||||
|
"position": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["left", "right"]
|
||||||
|
},
|
||||||
|
"handler": { "type": "string" }
|
||||||
|
},
|
||||||
|
"required": ["id", "label"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/permissions.json",
|
||||||
|
"title": "Verstak Permissions Registry",
|
||||||
|
"description": "Known runtime permissions and their safety levels",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"permissions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/PermissionEntry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"PermissionEntry": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"dangerous": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, user must explicitly approve before enabling plugin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "description", "dangerous"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
{ "name": "vault.read", "description": "Read vault files and metadata", "dangerous": false },
|
||||||
|
{ "name": "vault.write", "description": "Write vault files and metadata", "dangerous": true },
|
||||||
|
{ "name": "vault.watch", "description": "Watch vault file changes", "dangerous": false },
|
||||||
|
{ "name": "storage.namespace", "description": "Read/write plugin's own storage namespace", "dangerous": false },
|
||||||
|
{ "name": "storage.migrations", "description": "Run database migrations in plugin namespace", "dangerous": false },
|
||||||
|
{ "name": "events.publish", "description": "Publish events to the event bus", "dangerous": false },
|
||||||
|
{ "name": "events.subscribe", "description": "Subscribe to events on the event bus", "dangerous": false },
|
||||||
|
{ "name": "ui.register", "description": "Register UI components and contributions", "dangerous": false },
|
||||||
|
{ "name": "commands.register", "description": "Register command palette commands", "dangerous": false },
|
||||||
|
{ "name": "network.local", "description": "Connect to localhost network services", "dangerous": false },
|
||||||
|
{ "name": "network.remote", "description": "Connect to remote network services", "dangerous": true },
|
||||||
|
{ "name": "process.spawn", "description": "Spawn external processes", "dangerous": true },
|
||||||
|
{ "name": "secrets.read", "description": "Read secrets from the secret store", "dangerous": true },
|
||||||
|
{ "name": "secrets.write", "description": "Write secrets to the secret store", "dangerous": true },
|
||||||
|
{ "name": "sync.participate", "description": "Participate in vault sync", "dangerous": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://git.mirv.top/verstak/verstak-sdk/schemas/sync.json",
|
||||||
|
"title": "Verstak Sync Operations",
|
||||||
|
"description": "Sync operation schemas for vault synchronization between devices",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"SyncOperation": {
|
||||||
|
"$ref": "#/$defs/SyncOperation"
|
||||||
|
},
|
||||||
|
"SyncBatch": {
|
||||||
|
"$ref": "#/$defs/SyncBatch"
|
||||||
|
},
|
||||||
|
"SyncManifest": {
|
||||||
|
"$ref": "#/$defs/SyncManifest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"SyncOperation": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A single sync operation entry",
|
||||||
|
"required": ["op", "id", "timestamp"],
|
||||||
|
"properties": {
|
||||||
|
"op": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Operation type",
|
||||||
|
"enum": ["add", "modify", "delete", "rename"]
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unique operation identifier (UUID)"
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "ISO 8601 timestamp of the operation"
|
||||||
|
},
|
||||||
|
"deviceId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Originating device identifier"
|
||||||
|
},
|
||||||
|
"entityType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Entity type being synced",
|
||||||
|
"enum": ["file", "note", "plugin_state", "vault_meta"]
|
||||||
|
},
|
||||||
|
"entityPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Entity path relative to vault root"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Content hash (SHA-256) for content verification"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "File size in bytes"
|
||||||
|
},
|
||||||
|
"mimeType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "MIME type of the entity"
|
||||||
|
},
|
||||||
|
"pluginNamespace": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Plugin namespace, if syncing plugin state"
|
||||||
|
},
|
||||||
|
"oldPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Previous path for rename operations"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Arbitrary metadata key-value pairs",
|
||||||
|
"additionalProperties": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SyncBatch": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A batch of sync operations sent between devices",
|
||||||
|
"required": ["batchId", "deviceId", "operations", "timestamp"],
|
||||||
|
"properties": {
|
||||||
|
"batchId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Unique batch identifier (UUID)"
|
||||||
|
},
|
||||||
|
"deviceId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Originating device identifier"
|
||||||
|
},
|
||||||
|
"operations": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of sync operations in this batch",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/SyncOperation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "ISO 8601 timestamp of batch creation"
|
||||||
|
},
|
||||||
|
"lastSyncTimestamp": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Timestamp of the last successful sync from this device"
|
||||||
|
},
|
||||||
|
"sequence": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Sequence number for ordering batches"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SyncManifest": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Vault sync manifest for initial reconciliation",
|
||||||
|
"required": ["deviceId", "entries"],
|
||||||
|
"properties": {
|
||||||
|
"deviceId": { "type": "string" },
|
||||||
|
"entries": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["path", "hash", "updatedAt"],
|
||||||
|
"properties": {
|
||||||
|
"path": { "type": "string" },
|
||||||
|
"hash": { "type": "string" },
|
||||||
|
"size": { "type": "integer" },
|
||||||
|
"updatedAt": { "type": "string" },
|
||||||
|
"deleted": { "type": "boolean", "default": false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Conflict": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Conflict record when two devices modify the same entity",
|
||||||
|
"required": ["entityPath", "localHash", "remoteHash", "localTimestamp", "remoteTimestamp"],
|
||||||
|
"properties": {
|
||||||
|
"entityPath": { "type": "string" },
|
||||||
|
"localHash": { "type": "string" },
|
||||||
|
"remoteHash": { "type": "string" },
|
||||||
|
"localTimestamp": { "type": "string" },
|
||||||
|
"remoteTimestamp": { "type": "string" },
|
||||||
|
"resolution": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["local_wins", "remote_wins", "manual"],
|
||||||
|
"description": "How the conflict was resolved"
|
||||||
|
},
|
||||||
|
"resolvedAt": { "type": "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PairingRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Device pairing request payload",
|
||||||
|
"required": ["deviceName", "deviceType", "publicKey"],
|
||||||
|
"properties": {
|
||||||
|
"deviceName": { "type": "string" },
|
||||||
|
"deviceType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["desktop", "mobile"]
|
||||||
|
},
|
||||||
|
"publicKey": { "type": "string", "description": "Device public key for auth" },
|
||||||
|
"clientVersion": { "type": "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PairingResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Device pairing response",
|
||||||
|
"required": ["deviceId", "token", "pairedAt"],
|
||||||
|
"properties": {
|
||||||
|
"deviceId": { "type": "string" },
|
||||||
|
"token": { "type": "string", "description": "Auth token for this device" },
|
||||||
|
"pairedAt": { "type": "string" },
|
||||||
|
"serverVersion": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Verstak Plugin SDK — Public API
|
||||||
|
|
||||||
|
export * from './types';
|
||||||
|
export { VerstakPluginAPI, createPluginAPI } from './plugin-api';
|
||||||
|
export { RPCServer, RPCClient } from './rpc';
|
||||||
|
export {
|
||||||
|
createTestManifest,
|
||||||
|
createTestPluginState,
|
||||||
|
createMockPluginAPI,
|
||||||
|
validateManifest,
|
||||||
|
} from './test-utils';
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Verstak Plugin SDK — VerstakPluginAPI
|
||||||
|
// The official runtime API available to all plugins in the frontend context.
|
||||||
|
|
||||||
|
import type { PluginSettings } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VerstakPluginAPI — единственный способ для frontend плагина
|
||||||
|
* общаться с core платформы.
|
||||||
|
*
|
||||||
|
* Экземпляр API передаётся плагину при активации через глобальную
|
||||||
|
* переменную `window.__VERSTAK_PLUGIN_API__`.
|
||||||
|
*/
|
||||||
|
export class VerstakPluginAPI {
|
||||||
|
private pluginId: string;
|
||||||
|
private capabilities = new Set<string>();
|
||||||
|
|
||||||
|
constructor(pluginId: string) {
|
||||||
|
this.pluginId = pluginId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация API — вызывается core после загрузки frontend bundle.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
_init(capabilities: string[]): void {
|
||||||
|
this.capabilities = new Set(capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── View Registration ─────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать view для отображения в UI Shell.
|
||||||
|
*/
|
||||||
|
registerView(id: string, component: unknown): void {
|
||||||
|
this._postMessage('register.view', { id, component });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать панель настроек плагина.
|
||||||
|
*/
|
||||||
|
registerSettingsPanel(id: string, title: string, component: unknown): void {
|
||||||
|
this._postMessage('register.settingsPanel', { id, title, component });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать команду для command palette.
|
||||||
|
*/
|
||||||
|
registerCommand(id: string, title: string, handler: () => void, keybinding?: string): void {
|
||||||
|
this._postMessage('register.command', { id, title, keybinding, handler: handler.toString() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать действия для файлов.
|
||||||
|
*/
|
||||||
|
registerFileAction(id: string, label: string, handler: (filePath: string) => void, capability?: string): void {
|
||||||
|
this._postMessage('register.fileAction', { id, label, handler: handler.toString(), capability });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать действия для заметок.
|
||||||
|
*/
|
||||||
|
registerNoteAction(id: string, label: string, handler: (noteId: string) => void, capability?: string): void {
|
||||||
|
this._postMessage('register.noteAction', { id, label, handler: handler.toString(), capability });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать provider поиска.
|
||||||
|
*/
|
||||||
|
registerSearchProvider(id: string, label: string, handler: (query: string) => unknown[]): void {
|
||||||
|
this._postMessage('register.searchProvider', { id, label, handler: handler.toString() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Capabilities ──────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверить, доступна ли capability.
|
||||||
|
*/
|
||||||
|
hasCapability(name: string): boolean {
|
||||||
|
return this.capabilities.has(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получить список всех доступных capabilities.
|
||||||
|
*/
|
||||||
|
getAvailableCapabilities(): string[] {
|
||||||
|
return Array.from(this.capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Backend Communication ─────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вызвать backend метод плагина через RPC.
|
||||||
|
*/
|
||||||
|
async callBackend(method: string, args: unknown[] = []): Promise<unknown> {
|
||||||
|
return this._rpcCall(method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Settings ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Прочитать настройки плагина.
|
||||||
|
*/
|
||||||
|
async readSettings(): Promise<PluginSettings> {
|
||||||
|
const result = await this._rpcCall('readSettings', []);
|
||||||
|
return result as PluginSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Записать настройки плагина.
|
||||||
|
*/
|
||||||
|
async writeSettings(settings: PluginSettings): Promise<void> {
|
||||||
|
await this._rpcCall('writeSettings', [settings]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Event Bus ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Подписаться на событие event bus.
|
||||||
|
*/
|
||||||
|
subscribe(event: string, handler: (payload: unknown) => void): void {
|
||||||
|
this._postMessage('subscribe', { event, handler: handler.toString() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Опубликовать событие в event bus.
|
||||||
|
*/
|
||||||
|
publish(event: string, payload: unknown): void {
|
||||||
|
this._postMessage('publish', { event, payload });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Internal ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
private _postMessage(type: string, data: Record<string, unknown>): void {
|
||||||
|
window.dispatchEvent(new CustomEvent('verstak:plugin', {
|
||||||
|
detail: { pluginId: this.pluginId, type, data }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _rpcCall(method: string, args: unknown[]): Promise<unknown> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const callId = `${this.pluginId}:${Date.now()}:${Math.random()}`;
|
||||||
|
const handler = (event: CustomEvent) => {
|
||||||
|
if (event.detail.callId === callId) {
|
||||||
|
window.removeEventListener('verstak:rpc:response', handler as EventListener);
|
||||||
|
if (event.detail.error) {
|
||||||
|
reject(new Error(event.detail.error));
|
||||||
|
} else {
|
||||||
|
resolve(event.detail.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('verstak:rpc:response', handler as EventListener);
|
||||||
|
this._postMessage('rpc', { callId, method, args });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать экземпляр VerstakPluginAPI.
|
||||||
|
* Core вызывает эту функцию после загрузки frontend bundle,
|
||||||
|
* передавая pluginId и список доступных capabilities.
|
||||||
|
*/
|
||||||
|
export function createPluginAPI(pluginId: string): VerstakPluginAPI {
|
||||||
|
const api = new VerstakPluginAPI(pluginId);
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Verstak Plugin SDK — RPC Client for Sidecar Communication
|
||||||
|
|
||||||
|
export type RPCTransport = 'stdio' | 'tcp';
|
||||||
|
|
||||||
|
export interface RPCRequest {
|
||||||
|
jsonrpc: '2.0';
|
||||||
|
id: string;
|
||||||
|
method: string;
|
||||||
|
params: unknown[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RPCResponse {
|
||||||
|
jsonrpc: '2.0';
|
||||||
|
id: string;
|
||||||
|
result?: unknown;
|
||||||
|
error?: RPCError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RPCError {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
data?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RPC клиент для общения backend sidecar с core платформы.
|
||||||
|
* Использует JSON-RPC 2.0 протокол.
|
||||||
|
*/
|
||||||
|
export class RPCServer {
|
||||||
|
private handlers = new Map<string, (params: unknown[]) => Promise<unknown>>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зарегистрировать обработчик RPC метода.
|
||||||
|
*/
|
||||||
|
registerMethod(method: string, handler: (params: unknown[]) => Promise<unknown>): void {
|
||||||
|
this.handlers.set(method, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработать входящий RPC запрос.
|
||||||
|
*/
|
||||||
|
async handleRequest(request: RPCRequest): Promise<RPCResponse> {
|
||||||
|
const handler = this.handlers.get(request.method);
|
||||||
|
if (!handler) {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: request.id,
|
||||||
|
error: { code: -32601, message: `Method not found: ${request.method}` }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await handler(request.params);
|
||||||
|
return { jsonrpc: '2.0', id: request.id, result };
|
||||||
|
} catch (err) {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: request.id,
|
||||||
|
error: { code: -32000, message: err instanceof Error ? err.message : String(err) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать RPC запрос.
|
||||||
|
*/
|
||||||
|
createRequest(method: string, params: unknown[] = []): RPCRequest {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: `${Date.now()}:${Math.random()}`,
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Разобрать RPC ответ.
|
||||||
|
*/
|
||||||
|
parseResponse(data: string): RPCResponse {
|
||||||
|
return JSON.parse(data) as RPCResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RPC клиент (для core, вызывает методы sidecar).
|
||||||
|
*/
|
||||||
|
export class RPCClient {
|
||||||
|
private requestId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать JSON-RPC запрос.
|
||||||
|
*/
|
||||||
|
call(method: string, params: unknown[] = []): string {
|
||||||
|
const request: RPCRequest = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: `${++this.requestId}`,
|
||||||
|
method,
|
||||||
|
params
|
||||||
|
};
|
||||||
|
return JSON.stringify(request) + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Разобрать ответ.
|
||||||
|
*/
|
||||||
|
parseResponse(data: string): RPCResponse {
|
||||||
|
return JSON.parse(data.trim()) as RPCResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Verstak Plugin SDK — Test Utilities
|
||||||
|
|
||||||
|
import type { PluginManifest, PluginState } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать тестовый manifest для unit-тестов.
|
||||||
|
*/
|
||||||
|
export function createTestManifest(overrides?: Partial<PluginManifest>): PluginManifest {
|
||||||
|
return {
|
||||||
|
schemaVersion: 1,
|
||||||
|
id: 'test.plugin',
|
||||||
|
name: 'Test Plugin',
|
||||||
|
version: '0.1.0',
|
||||||
|
apiVersion: '1',
|
||||||
|
description: 'A test plugin for platform verification',
|
||||||
|
source: 'local',
|
||||||
|
provides: ['test.capability'],
|
||||||
|
requires: [],
|
||||||
|
optionalRequires: [],
|
||||||
|
permissions: ['events.publish', 'events.subscribe'],
|
||||||
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать тестовое состояние плагина.
|
||||||
|
*/
|
||||||
|
export function createTestPluginState(overrides?: Partial<PluginState>): PluginState {
|
||||||
|
return {
|
||||||
|
id: 'test.plugin',
|
||||||
|
manifest: createTestManifest(),
|
||||||
|
status: 'loaded',
|
||||||
|
enabled: true,
|
||||||
|
loadedAt: new Date().toISOString(),
|
||||||
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создать заглушку VerstakPluginAPI для тестов.
|
||||||
|
*/
|
||||||
|
export function createMockPluginAPI(): {
|
||||||
|
registerView: ReturnType<typeof vi.fn>;
|
||||||
|
registerCommand: ReturnType<typeof vi.fn>;
|
||||||
|
registerSettingsPanel: ReturnType<typeof vi.fn>;
|
||||||
|
hasCapability: ReturnType<typeof vi.fn>;
|
||||||
|
callBackend: ReturnType<typeof vi.fn>;
|
||||||
|
subscribe: ReturnType<typeof vi.fn>;
|
||||||
|
publish: ReturnType<typeof vi.fn>;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
registerView: vi.fn(),
|
||||||
|
registerCommand: vi.fn(),
|
||||||
|
registerSettingsPanel: vi.fn(),
|
||||||
|
hasCapability: vi.fn().mockReturnValue(false),
|
||||||
|
callBackend: vi.fn().mockResolvedValue(undefined),
|
||||||
|
subscribe: vi.fn(),
|
||||||
|
publish: vi.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Валидатор plugin manifest.
|
||||||
|
*/
|
||||||
|
export function validateManifest(manifest: unknown): { valid: boolean; errors: string[] } {
|
||||||
|
const errors: string[] = [];
|
||||||
|
|
||||||
|
if (!manifest || typeof manifest !== 'object') {
|
||||||
|
return { valid: false, errors: ['Manifest must be an object'] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = manifest as Record<string, unknown>;
|
||||||
|
|
||||||
|
if (m.schemaVersion !== 1) {
|
||||||
|
errors.push(`schemaVersion must be 1, got ${m.schemaVersion}`);
|
||||||
|
}
|
||||||
|
if (typeof m.id !== 'string' || !m.id) {
|
||||||
|
errors.push('id must be a non-empty string');
|
||||||
|
}
|
||||||
|
if (typeof m.name !== 'string' || !m.name) {
|
||||||
|
errors.push('name must be a non-empty string');
|
||||||
|
}
|
||||||
|
if (typeof m.version !== 'string' || !/^\d+\.\d+\.\d+/.test(m.version as string)) {
|
||||||
|
errors.push('version must be a valid semver (e.g. 0.1.0)');
|
||||||
|
}
|
||||||
|
if (typeof m.apiVersion !== 'string' || !m.apiVersion) {
|
||||||
|
errors.push('apiVersion must be a non-empty string');
|
||||||
|
}
|
||||||
|
if (!Array.isArray(m.provides) || m.provides.length === 0) {
|
||||||
|
errors.push('provides must be a non-empty array');
|
||||||
|
}
|
||||||
|
if (!Array.isArray(m.permissions) || m.permissions.length === 0) {
|
||||||
|
errors.push('permissions must be a non-empty array');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: errors.length === 0, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export vi for test files
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
export { vi };
|
||||||
|
|
@ -0,0 +1,345 @@
|
||||||
|
// Verstak Plugin SDK — Core TypeScript Types
|
||||||
|
|
||||||
|
// ─── Manifest ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type PluginSource = 'official' | 'local' | 'third-party';
|
||||||
|
|
||||||
|
export interface PluginManifest {
|
||||||
|
schemaVersion: 1;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
apiVersion: string;
|
||||||
|
description?: string;
|
||||||
|
source?: PluginSource;
|
||||||
|
icon?: string;
|
||||||
|
provides: string[];
|
||||||
|
requires?: string[];
|
||||||
|
optionalRequires?: string[];
|
||||||
|
permissions: Permission[];
|
||||||
|
frontend?: FrontendConfig;
|
||||||
|
backend?: BackendConfig;
|
||||||
|
migrations?: MigrationConfig;
|
||||||
|
contributes?: ContributionPoints;
|
||||||
|
sync?: SyncConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FrontendConfig {
|
||||||
|
entry: string;
|
||||||
|
style?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BackendConfig {
|
||||||
|
type: 'sidecar';
|
||||||
|
entry: Record<string, string>;
|
||||||
|
healthCheck?: HealthCheckConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HealthCheckConfig {
|
||||||
|
type?: 'rpc' | 'stdio' | 'tcp';
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MigrationConfig {
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SyncConfig {
|
||||||
|
namespaces?: string[];
|
||||||
|
participate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Capabilities ────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type CapabilityName = string;
|
||||||
|
|
||||||
|
export interface CapabilityEntry {
|
||||||
|
name: CapabilityName;
|
||||||
|
description: string;
|
||||||
|
status: 'stable' | 'draft' | 'deprecated';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Permissions ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type Permission =
|
||||||
|
| 'vault.read'
|
||||||
|
| 'vault.write'
|
||||||
|
| 'vault.watch'
|
||||||
|
| 'storage.namespace'
|
||||||
|
| 'storage.migrations'
|
||||||
|
| 'events.publish'
|
||||||
|
| 'events.subscribe'
|
||||||
|
| 'ui.register'
|
||||||
|
| 'commands.register'
|
||||||
|
| 'network.local'
|
||||||
|
| 'network.remote'
|
||||||
|
| 'process.spawn'
|
||||||
|
| 'secrets.read'
|
||||||
|
| 'secrets.write'
|
||||||
|
| 'sync.participate';
|
||||||
|
|
||||||
|
export interface PermissionEntry {
|
||||||
|
name: Permission;
|
||||||
|
description: string;
|
||||||
|
dangerous: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Contribution Points ─────────────────────────────────────
|
||||||
|
|
||||||
|
export interface ContributionPoints {
|
||||||
|
views?: ContributionView[];
|
||||||
|
commands?: ContributionCommand[];
|
||||||
|
settingsPanels?: ContributionSettingsPanel[];
|
||||||
|
sidebarItems?: ContributionSidebarItem[];
|
||||||
|
fileActions?: ContributionAction[];
|
||||||
|
noteActions?: ContributionAction[];
|
||||||
|
contextMenuEntries?: ContributionContextMenuEntry[];
|
||||||
|
searchProviders?: ContributionSearchProvider[];
|
||||||
|
activityProviders?: ContributionActivityProvider[];
|
||||||
|
statusBarItems?: ContributionStatusBarItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionView {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
component: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionCommand {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
keybinding?: string;
|
||||||
|
icon?: string;
|
||||||
|
handler?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionSettingsPanel {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
component: string;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionSidebarItem {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
view: string;
|
||||||
|
position?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionAction {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
icon?: string;
|
||||||
|
capability?: CapabilityName;
|
||||||
|
handler?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionContextMenuEntry {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
context: 'file' | 'note' | 'case' | 'folder';
|
||||||
|
group?: string;
|
||||||
|
capability?: CapabilityName;
|
||||||
|
handler?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionSearchProvider {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
handler: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionActivityProvider {
|
||||||
|
id: string;
|
||||||
|
events?: string[];
|
||||||
|
handler: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContributionStatusBarItem {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
position?: 'left' | 'right';
|
||||||
|
handler?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Plugin State ────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type PluginStatus =
|
||||||
|
| 'discovered'
|
||||||
|
| 'disabled'
|
||||||
|
| 'loading'
|
||||||
|
| 'loaded'
|
||||||
|
| 'degraded'
|
||||||
|
| 'failed'
|
||||||
|
| 'incompatible'
|
||||||
|
| 'missing-required-capability';
|
||||||
|
|
||||||
|
export interface PluginState {
|
||||||
|
id: string;
|
||||||
|
manifest: PluginManifest;
|
||||||
|
status: PluginStatus;
|
||||||
|
error?: string;
|
||||||
|
enabled: boolean;
|
||||||
|
loadedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Events ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface VerstakEvent {
|
||||||
|
name: string;
|
||||||
|
timestamp: string;
|
||||||
|
payload: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browser events
|
||||||
|
export interface BrowserCapturePageEvent extends VerstakEvent {
|
||||||
|
name: 'browser.capture.page';
|
||||||
|
payload: {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
html?: string;
|
||||||
|
text?: string;
|
||||||
|
capturedAt: string;
|
||||||
|
domain?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BrowserCaptureSelectionEvent extends VerstakEvent {
|
||||||
|
name: 'browser.capture.selection';
|
||||||
|
payload: {
|
||||||
|
url: string;
|
||||||
|
title: string;
|
||||||
|
text: string;
|
||||||
|
capturedAt: string;
|
||||||
|
domain?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BrowserCaptureLinkEvent extends VerstakEvent {
|
||||||
|
name: 'browser.capture.link';
|
||||||
|
payload: {
|
||||||
|
url: string;
|
||||||
|
title?: string;
|
||||||
|
capturedAt: string;
|
||||||
|
domain?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vault events
|
||||||
|
export interface VaultOpenedEvent extends VerstakEvent {
|
||||||
|
name: 'vault.opened';
|
||||||
|
payload: {
|
||||||
|
path: string;
|
||||||
|
version?: string;
|
||||||
|
openedAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CaseSelectedEvent extends VerstakEvent {
|
||||||
|
name: 'case.selected';
|
||||||
|
payload: {
|
||||||
|
caseId: string;
|
||||||
|
casePath: string;
|
||||||
|
caseType?: string;
|
||||||
|
selectedAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileChangedEvent extends VerstakEvent {
|
||||||
|
name: 'file.changed';
|
||||||
|
payload: {
|
||||||
|
path: string;
|
||||||
|
size?: number;
|
||||||
|
changedAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoteSavedEvent extends VerstakEvent {
|
||||||
|
name: 'note.saved';
|
||||||
|
payload: {
|
||||||
|
noteId: string;
|
||||||
|
title?: string;
|
||||||
|
path: string;
|
||||||
|
caseId?: string;
|
||||||
|
savedAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lifecycle events
|
||||||
|
export interface PluginEnabledEvent extends VerstakEvent {
|
||||||
|
name: 'plugin.enabled';
|
||||||
|
payload: {
|
||||||
|
pluginId: string;
|
||||||
|
version?: string;
|
||||||
|
enabledAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PluginDisabledEvent extends VerstakEvent {
|
||||||
|
name: 'plugin.disabled';
|
||||||
|
payload: {
|
||||||
|
pluginId: string;
|
||||||
|
disabledAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Sync Types ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type SyncOpType = 'add' | 'modify' | 'delete' | 'rename';
|
||||||
|
export type SyncEntityType = 'file' | 'note' | 'plugin_state' | 'vault_meta';
|
||||||
|
|
||||||
|
export interface SyncOperation {
|
||||||
|
op: SyncOpType;
|
||||||
|
id: string;
|
||||||
|
timestamp: string;
|
||||||
|
deviceId?: string;
|
||||||
|
entityType?: SyncEntityType;
|
||||||
|
entityPath?: string;
|
||||||
|
hash?: string;
|
||||||
|
size?: number;
|
||||||
|
mimeType?: string;
|
||||||
|
pluginNamespace?: string;
|
||||||
|
oldPath?: string;
|
||||||
|
metadata?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SyncBatch {
|
||||||
|
batchId: string;
|
||||||
|
deviceId: string;
|
||||||
|
operations: SyncOperation[];
|
||||||
|
timestamp: string;
|
||||||
|
lastSyncTimestamp?: string;
|
||||||
|
sequence?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SyncManifestEntry {
|
||||||
|
path: string;
|
||||||
|
hash: string;
|
||||||
|
size?: number;
|
||||||
|
updatedAt: string;
|
||||||
|
deleted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SyncManifest {
|
||||||
|
deviceId: string;
|
||||||
|
entries: SyncManifestEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Conflict {
|
||||||
|
entityPath: string;
|
||||||
|
localHash: string;
|
||||||
|
remoteHash: string;
|
||||||
|
localTimestamp: string;
|
||||||
|
remoteTimestamp: string;
|
||||||
|
resolution?: 'local_wins' | 'remote_wins' | 'manual';
|
||||||
|
resolvedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Settings ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface PluginSettings {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue