From 66c100454ca81d3f06a2aeb5b58f02ddb20f934f Mon Sep 17 00:00:00 2001 From: Jordon Date: Sun, 15 Feb 2026 15:28:49 +0000 Subject: [PATCH 01/12] Update --- Cargo.toml | 9 ++++----- src/lib.rs | 29 +++++++++++++++++++++++++++++ src/main.rs | 21 --------------------- 3 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 src/lib.rs delete mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 1d74b6d..70dbc6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,8 @@ name = "openvcs-plugin-template" version = "0.1.0" edition = "2024" -[dependencies] -openvcs-core = { version = "0.1", features = ["plugin-protocol"] } +[lib] +crate-type = ["cdylib", "rlib"] -[[bin]] -name = "hello-world-plugin" -path = "src/main.rs" +[dependencies] +openvcs-core = { path = "../Core", features = ["plugin-protocol"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b02abe5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,29 @@ +//! OpenVCS Plugin Template +//! +//! A minimal plugin that demonstrates the plugin ABI structure. + +use openvcs_core::app_api::PluginError; +use openvcs_core::openvcs_plugin; + +// Internal helpers - NOT ABI +#[allow(dead_code)] +fn helper() -> String { + "Hello from internal helper!".to_string() +} + +// Plugin ABI - functions in mod plugin are exported +#[openvcs_plugin] +mod plugin { + use super::*; + + pub fn init() -> Result<(), PluginError> { + Ok(()) + } + + pub fn deinit() -> Result<(), PluginError> { + Ok(()) + } +} + +// Generate WIT Guest impl +openvcs_core::export_plugin!(plugin); diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index c384ae7..0000000 --- a/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use openvcs_core::models::Capabilities; -use openvcs_core::plugin_protocol::RpcRequest; -use openvcs_core::plugin_runtime::{ - HandlerResult, PluginCtx, register_delegate, run_registered, set_fallback_rpc, -}; -use openvcs_core::plugin_stdio::{ok, ok_null}; -use openvcs_core::info; - -fn caps(_ctx: &mut PluginCtx, _req: RpcRequest) -> HandlerResult { - ok(Capabilities::default()) -} - -fn main() -> std::io::Result<()> { - // Runs once when OpenVCS starts the plugin. - info!("Hello, World from PluginTemplate!"); - - // Minimal stub RPC so OpenVCS can safely query module metadata without errors. - register_delegate("caps", caps); - set_fallback_rpc(|_ctx, _req| ok_null()); - run_registered() -} From 215937e9e7c9d31527b2369057247a6f89fa526f Mon Sep 17 00:00:00 2001 From: Jordon Date: Sun, 15 Feb 2026 15:49:21 +0000 Subject: [PATCH 02/12] Update lib.rs --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b02abe5..8a93542 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ //! A minimal plugin that demonstrates the plugin ABI structure. use openvcs_core::app_api::PluginError; +use openvcs_core::info; use openvcs_core::openvcs_plugin; // Internal helpers - NOT ABI @@ -17,6 +18,7 @@ mod plugin { use super::*; pub fn init() -> Result<(), PluginError> { + info!("Hello, World!"); Ok(()) } From 11d7bbf0b9639bd0e3f9a50f6ef8b059eaecfbdb Mon Sep 17 00:00:00 2001 From: Jordon Date: Sun, 15 Feb 2026 16:09:16 +0000 Subject: [PATCH 03/12] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ae7628d..08a521a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This folder is a starter template for building OpenVCS plugins. -It includes a single **WASI/Rust module** (`src/main.rs`) that prints a message when the plugin starts. +It includes a single **WASI/Rust library** (`src/lib.rs`) that prints a message when the plugin starts. ## Build (`.ovcsp`) @@ -31,14 +31,15 @@ Notes: ## Files - `openvcs.plugin.json`: Plugin manifest (id + module exec). -- `Cargo.toml`, `src/main.rs`: Rust/WASI module executable. +- `Cargo.toml`, `src/lib.rs`: Rust/WASI library. +- `src/lib.rs` - Rust library with `#[openvcs_plugin]` and `export_plugin!` macros ## Customizing for your own plugin 1. Pick a new plugin id (example: `com.yourname.my-plugin`). 2. Update these in sync: - - `PluginTemplate/openvcs.plugin.json` (`id`, and any file names you change) - - `PluginTemplate/src/main.rs` (the startup message and any method names you add) + - `PluginTemplate/openvcs.plugin.json` (`id`, and any file names you change) + - `PluginTemplate/src/lib.rs` (the startup message and any method names you add) 3. If you rename the module executable, update: - `PluginTemplate/openvcs.plugin.json` → `module.exec` - `PluginTemplate/Cargo.toml` → `[[bin]].name` From f361ca4ac36f79642aaa502e6cd9309d5b5c68b1 Mon Sep 17 00:00:00 2001 From: Jordon Date: Wed, 18 Feb 2026 23:48:43 +0000 Subject: [PATCH 04/12] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 70dbc6c..6595754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [lib] -crate-type = ["cdylib", "rlib"] +crate-type = ["rlib"] [dependencies] openvcs-core = { path = "../Core", features = ["plugin-protocol"] } From 8ce2e90c607360b0924e4f63e46bca223088053e Mon Sep 17 00:00:00 2001 From: Jordon Date: Wed, 18 Feb 2026 23:57:47 +0000 Subject: [PATCH 05/12] Revert "Update Cargo.toml" This reverts commit f361ca4ac36f79642aaa502e6cd9309d5b5c68b1. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6595754..70dbc6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [lib] -crate-type = ["rlib"] +crate-type = ["cdylib", "rlib"] [dependencies] openvcs-core = { path = "../Core", features = ["plugin-protocol"] } From dbd76fb0ac056447acaee144707bc27357237762 Mon Sep 17 00:00:00 2001 From: Jordon Date: Wed, 18 Feb 2026 23:58:14 +0000 Subject: [PATCH 06/12] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 70dbc6c..4b0cbb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [lib] -crate-type = ["cdylib", "rlib"] +crate-type = ["cdylib"] [dependencies] openvcs-core = { path = "../Core", features = ["plugin-protocol"] } From 6bcd0efcb549de5f31e7357c39ff07ff99d8ff71 Mon Sep 17 00:00:00 2001 From: Jordon Date: Thu, 19 Feb 2026 08:30:44 +0000 Subject: [PATCH 07/12] Update docs --- ARCHITECTURE.md | 2 +- PLANS.md | 2 +- README.md | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d6a6215..7c34ff4 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -17,7 +17,7 @@ It demonstrates: - `openvcs.plugin.json`: plugin manifest baseline. - `Cargo.toml`: crate and binary configuration. -- `src/main.rs`: starter module logic. +- `src/lib.rs`: starter module logic. - `dist/`: output location for packaged bundles. ## Boundaries diff --git a/PLANS.md b/PLANS.md index c38d4cb..fd8a61f 100644 --- a/PLANS.md +++ b/PLANS.md @@ -139,7 +139,7 @@ Prefer additive code changes followed by subtractions that keep tests passing. P Be prescriptive. Name the libraries, modules, and services to use and why. Specify the types, traits/interfaces, and function signatures that must exist at the end of the milestone. Prefer stable names and paths such as `crate::module::function` or `package.submodule.Interface`. E.g.: - In crates/foo/planner.rs, define: + In src/foo/planner.rs, define: pub trait Planner { fn plan(&self, observed: &Observed) -> Vec; diff --git a/README.md b/README.md index 08a521a..9dbf7ba 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This bundles the plugin into `dist/*.ovcsp`. If `cargo openvcs` is not installed, you can run the SDK packager directly: ```bash -cargo run --manifest-path ../OpenVCS-SDK/Cargo.toml --bin openvcs-plugin -- --plugin-dir PluginTemplate --out PluginTemplate/dist +cargo run --manifest-path ../SDK/Cargo.toml --bin openvcs-plugin -- --plugin-dir . --out dist ``` Notes: @@ -40,6 +40,4 @@ Notes: 2. Update these in sync: - `PluginTemplate/openvcs.plugin.json` (`id`, and any file names you change) - `PluginTemplate/src/lib.rs` (the startup message and any method names you add) -3. If you rename the module executable, update: - - `PluginTemplate/openvcs.plugin.json` → `module.exec` - - `PluginTemplate/Cargo.toml` → `[[bin]].name` +3. If you rename the bundled module filename, update `PluginTemplate/openvcs.plugin.json` → `module.exec`. From f8f93b9da8a9d3e5e2c32a26f143ba8618519b25 Mon Sep 17 00:00:00 2001 From: Jordon Date: Thu, 19 Feb 2026 11:31:17 +0000 Subject: [PATCH 08/12] Update --- README.md | 11 +++++++++-- src/lib.rs | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9dbf7ba..82b65c6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This folder is a starter template for building OpenVCS plugins. It includes a single **WASI/Rust library** (`src/lib.rs`) that prints a message when the plugin starts. +The template uses `use openvcs_core::prelude::*;` so plugin code can stay concise. ## Build (`.ovcsp`) @@ -17,7 +18,13 @@ This bundles the plugin into `dist/*.ovcsp`. If `cargo openvcs` is not installed, you can run the SDK packager directly: ```bash -cargo run --manifest-path ../SDK/Cargo.toml --bin openvcs-plugin -- --plugin-dir . --out dist +cargo run --manifest-path ../SDK/Cargo.toml --bin cargo-openvcs -- dist --plugin-dir . --out dist +``` + +Preferred install path: + +```bash +cargo install openvcs-sdk ``` Notes: @@ -32,7 +39,7 @@ Notes: - `openvcs.plugin.json`: Plugin manifest (id + module exec). - `Cargo.toml`, `src/lib.rs`: Rust/WASI library. -- `src/lib.rs` - Rust library with `#[openvcs_plugin]` and `export_plugin!` macros +- `src/lib.rs` - Rust library using `openvcs_core::prelude::*` plus `#[openvcs_plugin]` and `export_plugin!` ## Customizing for your own plugin diff --git a/src/lib.rs b/src/lib.rs index 8a93542..1e0e83e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,11 @@ //! //! A minimal plugin that demonstrates the plugin ABI structure. -use openvcs_core::app_api::PluginError; -use openvcs_core::info; -use openvcs_core::openvcs_plugin; +use openvcs_core::prelude::*; // Internal helpers - NOT ABI #[allow(dead_code)] +/// Returns a sample helper string used by the template. fn helper() -> String { "Hello from internal helper!".to_string() } @@ -17,11 +16,19 @@ fn helper() -> String { mod plugin { use super::*; + /// Initializes the template plugin. + /// + /// # Returns + /// - `Ok(())` when plugin startup succeeds. pub fn init() -> Result<(), PluginError> { info!("Hello, World!"); Ok(()) } + /// Deinitializes the template plugin. + /// + /// # Returns + /// - `Ok(())` when plugin shutdown succeeds. pub fn deinit() -> Result<(), PluginError> { Ok(()) } From 05f4dcafe1b8fda32b69976169803f27e7b18b08 Mon Sep 17 00:00:00 2001 From: Jordon Date: Mon, 16 Mar 2026 16:28:46 +0000 Subject: [PATCH 09/12] Update Readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 82b65c6..a60b635 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,21 @@ The template uses `use openvcs_core::prelude::*;` so plugin code can stay concis From `PluginTemplate/`, run: ```bash -cargo openvcs dist +npx --package @openvcs/sdk openvcs dist --plugin-dir . --out dist ``` This bundles the plugin into `dist/*.ovcsp`. -If `cargo openvcs` is not installed, you can run the SDK packager directly: +Preferred install path for repeated use: ```bash -cargo run --manifest-path ../SDK/Cargo.toml --bin cargo-openvcs -- dist --plugin-dir . --out dist +npm install --save-dev @openvcs/sdk ``` -Preferred install path: +Then run: ```bash -cargo install openvcs-sdk +npx openvcs dist --plugin-dir . --out dist ``` Notes: From 82a5b4683f14b0bf72b8178ff6bf56a72414be14 Mon Sep 17 00:00:00 2001 From: Jordon Date: Mon, 6 Apr 2026 00:10:06 +0100 Subject: [PATCH 10/12] Update --- ARCHITECTURE.md | 4 +- PLANS.md | 150 ------------------------------------------------ README.md | 14 ++--- 3 files changed, 9 insertions(+), 159 deletions(-) delete mode 100644 PLANS.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7c34ff4..7f5233a 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -11,11 +11,11 @@ See umbrella context in `../ARCHITECTURE.md`. It demonstrates: - manifest layout, - module entrypoint layout, -- packaging path to `.ovcsp`. +- npm package path for config-based plugin resolution. ## Code Map -- `openvcs.plugin.json`: plugin manifest baseline. +- `package.json`: npm manifest with `openvcs` plugin metadata. - `Cargo.toml`: crate and binary configuration. - `src/lib.rs`: starter module logic. - `dist/`: output location for packaged bundles. diff --git a/PLANS.md b/PLANS.md deleted file mode 100644 index fd8a61f..0000000 --- a/PLANS.md +++ /dev/null @@ -1,150 +0,0 @@ -# Codex Execution Plans (ExecPlans): - -This document describes the requirements for an execution plan ("ExecPlan"), a design document that a coding agent can follow to deliver a working feature or system change. Treat the reader as a complete beginner to this repository: they have only the current working tree and the single ExecPlan file you provide. There is no memory of prior plans and no external context. - -## How to use ExecPlans and PLANS.md - -When authoring an executable specification (ExecPlan), follow PLANS.md _to the letter_. If it is not in your context, refresh your memory by reading the entire PLANS.md file. Be thorough in reading (and re-reading) source material to produce an accurate specification. When creating a spec, start from the skeleton and flesh it out as you do your research. - -When implementing an executable specification (ExecPlan), do not prompt the user for "next steps"; simply proceed to the next milestone. Keep all sections up to date, add or split entries in the list at every stopping point to affirmatively state the progress made and next steps. Resolve ambiguities autonomously, and commit frequently. - -When discussing an executable specification (ExecPlan), record decisions in a log in the spec for posterity; it should be unambiguously clear why any change to the specification was made. ExecPlans are living documents, and it should always be possible to restart from _only_ the ExecPlan and no other work. - -When researching a design with challenging requirements or significant unknowns, use milestones to implement proof of concepts, "toy implementations", etc., that allow validating whether the user's proposal is feasible. Read the source code of libraries by finding or acquiring them, research deeply, and include prototypes to guide a fuller implementation. - -## Requirements - -NON-NEGOTIABLE REQUIREMENTS: - -* Every ExecPlan must be fully self-contained. Self-contained means that in its current form it contains all knowledge and instructions needed for a novice to succeed. -* Every ExecPlan is a living document. Contributors are required to revise it as progress is made, as discoveries occur, and as design decisions are finalized. Each revision must remain fully self-contained. -* Every ExecPlan must enable a complete novice to implement the feature end-to-end without prior knowledge of this repo. -* Every ExecPlan must produce a demonstrably working behavior, not merely code changes to "meet a definition". -* Every ExecPlan must define every term of art in plain language or do not use it. - -Purpose and intent come first. Begin by explaining, in a few sentences, why the work matters from a user's perspective: what someone can do after this change that they could not do before, and how to see it working. Then guide the reader through the exact steps to achieve that outcome, including what to edit, what to run, and what they should observe. - -The agent executing your plan can list files, read files, search, run the project, and run tests. It does not know any prior context and cannot infer what you meant from earlier milestones. Repeat any assumption you rely on. Do not point to external blogs or docs; if knowledge is required, embed it in the plan itself in your own words. If an ExecPlan builds upon a prior ExecPlan and that file is checked in, incorporate it by reference. If it is not, you must include all relevant context from that plan. - -## Formatting - -Format and envelope are simple and strict. Each ExecPlan must be one single fenced code block labeled as `md` that begins and ends with triple backticks. Do not nest additional triple-backtick code fences inside; when you need to show commands, transcripts, diffs, or code, present them as indented blocks within that single fence. Use indentation for clarity rather than code fences inside an ExecPlan to avoid prematurely closing the ExecPlan's code fence. Use two newlines after every heading, use # and ## and so on, and correct syntax for ordered and unordered lists. - -When writing an ExecPlan to a Markdown (.md) file where the content of the file *is only* the single ExecPlan, you should omit the triple backticks. - -Write in plain prose. Prefer sentences over lists. Avoid checklists, tables, and long enumerations unless brevity would obscure meaning. Checklists are permitted only in the `Progress` section, where they are mandatory. Narrative sections must remain prose-first. - -## Guidelines - -Self-containment and plain language are paramount. If you introduce a phrase that is not ordinary English ("daemon", "middleware", "RPC gateway", "filter graph"), define it immediately and remind the reader how it manifests in this repository (for example, by naming the files or commands where it appears). Do not say "as defined previously" or "according to the architecture doc." Include the needed explanation here, even if you repeat yourself. - -Avoid common failure modes. Do not rely on undefined jargon. Do not describe "the letter of a feature" so narrowly that the resulting code compiles but does nothing meaningful. Do not outsource key decisions to the reader. When ambiguity exists, resolve it in the plan itself and explain why you chose that path. Err on the side of over-explaining user-visible effects and under-specifying incidental implementation details. - -Anchor the plan with observable outcomes. State what the user can do after implementation, the commands to run, and the outputs they should see. Acceptance should be phrased as behavior a human can verify ("after starting the server, navigating to [http://localhost:8080/health](http://localhost:8080/health) returns HTTP 200 with body OK") rather than internal attributes ("added a HealthCheck struct"). If a change is internal, explain how its impact can still be demonstrated (for example, by running tests that fail before and pass after, and by showing a scenario that uses the new behavior). - -Specify repository context explicitly. Name files with full repository-relative paths, name functions and modules precisely, and describe where new files should be created. If touching multiple areas, include a short orientation paragraph that explains how those parts fit together so a novice can navigate confidently. When running commands, show the working directory and exact command line. When outcomes depend on environment, state the assumptions and provide alternatives when reasonable. - -Be idempotent and safe. Write the steps so they can be run multiple times without causing damage or drift. If a step can fail halfway, include how to retry or adapt. If a migration or destructive operation is necessary, spell out backups or safe fallbacks. Prefer additive, testable changes that can be validated as you go. - -Validation is not optional. Include instructions to run tests, to start the system if applicable, and to observe it doing something useful. Describe comprehensive testing for any new features or capabilities. Include expected outputs and error messages so a novice can tell success from failure. Where possible, show how to prove that the change is effective beyond compilation (for example, through a small end-to-end scenario, a CLI invocation, or an HTTP request/response transcript). State the exact test commands appropriate to the project’s toolchain and how to interpret their results. - -Capture evidence. When your steps produce terminal output, short diffs, or logs, include them inside the single fenced block as indented examples. Keep them concise and focused on what proves success. If you need to include a patch, prefer file-scoped diffs or small excerpts that a reader can recreate by following your instructions rather than pasting large blobs. - -## Milestones - -Milestones are narrative, not bureaucracy. If you break the work into milestones, introduce each with a brief paragraph that describes the scope, what will exist at the end of the milestone that did not exist before, the commands to run, and the acceptance you expect to observe. Keep it readable as a story: goal, work, result, proof. Progress and milestones are distinct: milestones tell the story, progress tracks granular work. Both must exist. Never abbreviate a milestone merely for the sake of brevity, do not leave out details that could be crucial to a future implementation. - -Each milestone must be independently verifiable and incrementally implement the overall goal of the execution plan. - -## Living plans and design decisions - -* ExecPlans are living documents. As you make key design decisions, update the plan to record both the decision and the thinking behind it. Record all decisions in the `Decision Log` section. -* ExecPlans must contain and maintain a `Progress` section, a `Surprises & Discoveries` section, a `Decision Log`, and an `Outcomes & Retrospective` section. These are not optional. -* When you discover optimizer behavior, performance tradeoffs, unexpected bugs, or inverse/unapply semantics that shaped your approach, capture those observations in the `Surprises & Discoveries` section with short evidence snippets (test output is ideal). -* If you change course mid-implementation, document why in the `Decision Log` and reflect the implications in `Progress`. Plans are guides for the next contributor as much as checklists for you. -* At completion of a major task or the full plan, write an `Outcomes & Retrospective` entry summarizing what was achieved, what remains, and lessons learned. - -# Prototyping milestones and parallel implementations - -It is acceptable—-and often encouraged—-to include explicit prototyping milestones when they de-risk a larger change. Examples: adding a low-level operator to a dependency to validate feasibility, or exploring two composition orders while measuring optimizer effects. Keep prototypes additive and testable. Clearly label the scope as “prototyping”; describe how to run and observe results; and state the criteria for promoting or discarding the prototype. - -Prefer additive code changes followed by subtractions that keep tests passing. Parallel implementations (e.g., keeping an adapter alongside an older path during migration) are fine when they reduce risk or enable tests to continue passing during a large migration. Describe how to validate both paths and how to retire one safely with tests. When working with multiple new libraries or feature areas, consider creating spikes that evaluate the feasibility of these features _independently_ of one another, proving that the external library performs as expected and implements the features we need in isolation. - -## Skeleton of a Good ExecPlan - - # - - This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds. - - If PLANS.md file is checked into the repo, reference the path to that file here from the repository root and note that this document must be maintained in accordance with PLANS.md. - - ## Purpose / Big Picture - - Explain in a few sentences what someone gains after this change and how they can see it working. State the user-visible behavior you will enable. - - ## Progress - - Use a list with checkboxes to summarize granular steps. Every stopping point must be documented here, even if it requires splitting a partially completed task into two (“done” vs. “remaining”). This section must always reflect the actual current state of the work. - - - [x] (2025-10-01 13:00Z) Example completed step. - - [ ] Example incomplete step. - - [ ] Example partially completed step (completed: X; remaining: Y). - - Use timestamps to measure rates of progress. - - ## Surprises & Discoveries - - Document unexpected behaviors, bugs, optimizations, or insights discovered during implementation. Provide concise evidence. - - - Observation: … - Evidence: … - - ## Decision Log - - Record every decision made while working on the plan in the format: - - - Decision: … - Rationale: … - Date/Author: … - - ## Outcomes & Retrospective - - Summarize outcomes, gaps, and lessons learned at major milestones or at completion. Compare the result against the original purpose. - - ## Context and Orientation - - Describe the current state relevant to this task as if the reader knows nothing. Name the key files and modules by full path. Define any non-obvious term you will use. Do not refer to prior plans. - - ## Plan of Work - - Describe, in prose, the sequence of edits and additions. For each edit, name the file and location (function, module) and what to insert or change. Keep it concrete and minimal. - - ## Concrete Steps - - State the exact commands to run and where to run them (working directory). When a command generates output, show a short expected transcript so the reader can compare. This section must be updated as work proceeds. - - ## Validation and Acceptance - - Describe how to start or exercise the system and what to observe. Phrase acceptance as behavior, with specific inputs and outputs. If tests are involved, say "run and expect passed; the new test fails before the change and passes after>". - - ## Idempotence and Recovery - - If steps can be repeated safely, say so. If a step is risky, provide a safe retry or rollback path. Keep the environment clean after completion. - - ## Artifacts and Notes - - Include the most important transcripts, diffs, or snippets as indented examples. Keep them concise and focused on what proves success. - - ## Interfaces and Dependencies - - Be prescriptive. Name the libraries, modules, and services to use and why. Specify the types, traits/interfaces, and function signatures that must exist at the end of the milestone. Prefer stable names and paths such as `crate::module::function` or `package.submodule.Interface`. E.g.: - - In src/foo/planner.rs, define: - - pub trait Planner { - fn plan(&self, observed: &Observed) -> Vec; - } - -If you follow the guidance above, a single, stateless agent -- or a human novice -- can read your ExecPlan from top to bottom and produce a working, observable result. That is the bar: SELF-CONTAINED, SELF-SUFFICIENT, NOVICE-GUIDING, OUTCOME-FOCUSED. - -When you revise a plan, you must ensure your changes are comprehensively reflected across all sections, including the living document sections, and you must write a note at the bottom of the plan describing the change and the reason why. ExecPlans must describe not just the what but the why for almost everything. diff --git a/README.md b/README.md index a60b635..43a361f 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ This folder is a starter template for building OpenVCS plugins. It includes a single **WASI/Rust library** (`src/lib.rs`) that prints a message when the plugin starts. The template uses `use openvcs_core::prelude::*;` so plugin code can stay concise. -## Build (`.ovcsp`) +## Build From `PluginTemplate/`, run: ```bash -npx --package @openvcs/sdk openvcs dist --plugin-dir . --out dist +npx --package @openvcs/sdk openvcs build --plugin-dir . ``` -This bundles the plugin into `dist/*.ovcsp`. +This builds the plugin runtime assets in place. Preferred install path for repeated use: @@ -24,7 +24,7 @@ npm install --save-dev @openvcs/sdk Then run: ```bash -npx openvcs dist --plugin-dir . --out dist +npx openvcs build --plugin-dir . ``` Notes: @@ -37,7 +37,7 @@ Notes: ## Files -- `openvcs.plugin.json`: Plugin manifest (id + module exec). +- `package.json`: npm manifest with `openvcs` plugin metadata. - `Cargo.toml`, `src/lib.rs`: Rust/WASI library. - `src/lib.rs` - Rust library using `openvcs_core::prelude::*` plus `#[openvcs_plugin]` and `export_plugin!` @@ -45,6 +45,6 @@ Notes: 1. Pick a new plugin id (example: `com.yourname.my-plugin`). 2. Update these in sync: - - `PluginTemplate/openvcs.plugin.json` (`id`, and any file names you change) + - `PluginTemplate/package.json` (`openvcs.id`, and any file names you change) - `PluginTemplate/src/lib.rs` (the startup message and any method names you add) -3. If you rename the bundled module filename, update `PluginTemplate/openvcs.plugin.json` → `module.exec`. +3. If you rename the bundled module filename, update `PluginTemplate/package.json` → `openvcs.module.exec`. From 98f771ca4514eda5c71f343f753ab4689f0eec25 Mon Sep 17 00:00:00 2001 From: Jordon Date: Sun, 26 Apr 2026 07:56:33 +0100 Subject: [PATCH 11/12] Implement new architecture --- .cargo/config.toml | 2 - .gitignore | 6 +- ARCHITECTURE.md | 39 --- Cargo.toml | 10 - README.md | 50 ++-- openvcs.plugin.json | 12 - package-lock.json | 609 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 52 ++++ src/lib.rs | 38 --- src/plugin.ts | 17 ++ test/plugin.test.ts | 38 +++ tsconfig.json | 16 ++ 12 files changed, 753 insertions(+), 136 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 ARCHITECTURE.md delete mode 100644 Cargo.toml delete mode 100644 openvcs.plugin.json create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 src/lib.rs create mode 100644 src/plugin.ts create mode 100644 test/plugin.test.ts create mode 100644 tsconfig.json diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 6b509f5..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-wasip1" diff --git a/.gitignore b/.gitignore index 39f9e48..0cecc59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/dist -/target -/Cargo.lock +/bin +/node_modules +/tsconfig.tsbuildinfo diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 7f5233a..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,39 +0,0 @@ -# PluginTemplate Architecture - -This document describes the starter plugin scaffold in `PluginTemplate/`. - -See umbrella context in `../ARCHITECTURE.md`. - -## Bird's Eye View - -`PluginTemplate/` is a minimal baseline for creating a new OpenVCS plugin. - -It demonstrates: -- manifest layout, -- module entrypoint layout, -- npm package path for config-based plugin resolution. - -## Code Map - -- `package.json`: npm manifest with `openvcs` plugin metadata. -- `Cargo.toml`: crate and binary configuration. -- `src/lib.rs`: starter module logic. -- `dist/`: output location for packaged bundles. - -## Boundaries - -- Template is a scaffold, not a feature-complete reference implementation. -- Runtime plugin hosting and capability enforcement are owned by `Client/Backend`. -- Shared protocol and trait contracts are owned by `Core/`. - -## Architecture Invariants - -- Template should stay minimal and easy to clone. -- Renaming plugin id/entrypoint should be explicit and localized to manifest + cargo/bin names. - -## Cross-Cutting Concerns - -- Onboarding: - The template should optimize first-time plugin author experience. -- Consistency: - Template structure should remain compatible with SDK packaging and host runtime expectations. diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 4b0cbb6..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "openvcs-plugin-template" -version = "0.1.0" -edition = "2024" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -openvcs-core = { path = "../Core", features = ["plugin-protocol"] } diff --git a/README.md b/README.md index 43a361f..077dfd5 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,36 @@ # PluginTemplate: Hello, World! -This folder is a starter template for building OpenVCS plugins. +This folder is a starter template for building OpenVCS plugins with the same SDK/runtime architecture used by the Git plugin. -It includes a single **WASI/Rust library** (`src/lib.rs`) that prints a message when the plugin starts. -The template uses `use openvcs_core::prelude::*;` so plugin code can stay concise. +It keeps the behavior simple: when the plugin starts, it logs one line: + +- `Hello, World from PluginTemplate!` ## Build From `PluginTemplate/`, run: ```bash -npx --package @openvcs/sdk openvcs build --plugin-dir . +npm install +npm run build ``` -This builds the plugin runtime assets in place. - -Preferred install path for repeated use: +## Validate ```bash -npm install --save-dev @openvcs/sdk +npm run lint +npm test ``` -Then run: - -```bash -npx openvcs build --plugin-dir . -``` - -Notes: -- These plugins build for `wasm32-wasip1`. If needed: `rustup target add wasm32-wasip1`. - -## What it does - -- When enabled/loaded, it logs one line: - - `Hello, World from PluginTemplate!` - -## Files +## What it contains -- `package.json`: npm manifest with `openvcs` plugin metadata. -- `Cargo.toml`, `src/lib.rs`: Rust/WASI library. -- `src/lib.rs` - Rust library using `openvcs_core::prelude::*` plus `#[openvcs_plugin]` and `export_plugin!` +- `package.json`: OpenVCS plugin manifest and npm scripts. +- `tsconfig.json`: TypeScript build settings for the plugin runtime. +- `src/plugin.ts`: plugin definition and startup hook. +- `test/plugin.test.ts`: basic smoke tests for the exported plugin contract. -## Customizing for your own plugin +## Customizing -1. Pick a new plugin id (example: `com.yourname.my-plugin`). -2. Update these in sync: - - `PluginTemplate/package.json` (`openvcs.id`, and any file names you change) - - `PluginTemplate/src/lib.rs` (the startup message and any method names you add) -3. If you rename the bundled module filename, update `PluginTemplate/package.json` → `openvcs.module.exec`. +1. Change the `openvcs.id` in `package.json`. +2. Update the startup log line in `src/plugin.ts`. +3. Add more plugin behavior using the same `PluginDefinition` / `OnPluginStart()` entrypoint pattern. diff --git a/openvcs.plugin.json b/openvcs.plugin.json deleted file mode 100644 index 2ca3fee..0000000 --- a/openvcs.plugin.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "template.hello-world", - "name": "Template: Hello World", - "author": "OpenVCS", - "category": "Templates", - "description": "Minimal plugin template: logs a Hello World message on load.", - "version": "0.1.0", - "default_enabled": false, - "module": { - "exec": "hello-world-plugin.wasm" - } -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2114aa0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,609 @@ +{ + "name": "@openvcs/plugin-template", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@openvcs/plugin-template", + "version": "0.1.0", + "license": "GPL-3.0-or-later", + "dependencies": { + "@openvcs/sdk": "edge" + }, + "devDependencies": { + "@types/node": "^25.5.0", + "tsx": "^4.20.6", + "typescript": "^6.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@openvcs/sdk": { + "version": "0.3.0-edge.20260423.33", + "resolved": "https://registry.npmjs.org/@openvcs/sdk/-/sdk-0.3.0-edge.20260423.33.tgz", + "integrity": "sha512-7s4+osrD/FJzarh6IFggElimw2UbYRRDKWusY/TZwcst+uoH3cfc/+pUdmy7/5m6BWi601H/9IU+Wfy1YlGTsQ==", + "license": "GPL-3.0-or-later", + "bin": { + "openvcs": "bin/openvcs.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7fde1c4 --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "@openvcs/plugin-template", + "version": "0.1.0", + "description": "OpenVCS plugin template using the Node.js SDK runtime", + "license": "GPL-3.0-or-later", + "homepage": "https://github.com/Open-VCS/PluginTemplate", + "repository": { + "type": "git", + "url": "https://github.com/Open-VCS/PluginTemplate" + }, + "bugs": { + "url": "https://github.com/Open-VCS/PluginTemplate/issues" + }, + "type": "module", + "engines": { + "node": ">=18" + }, + "openvcs": { + "id": "template.hello-world", + "name": "Template: Hello World", + "version": "0.1.0", + "author": "OpenVCS Contributors", + "description": "Minimal plugin template: logs a Hello World message on load.", + "default_enabled": false, + "module": { + "exec": "openvcs-template-plugin.js" + } + }, + "scripts": { + "lint": "tsc -p tsconfig.json --noEmit", + "test": "tsx --test test/*.test.ts", + "prepack": "npm run build", + "build:plugin": "tsc -p tsconfig.json", + "build": "node ./node_modules/@openvcs/sdk/bin/openvcs.js build" + }, + "dependencies": { + "@openvcs/sdk": "edge" + }, + "devDependencies": { + "@types/node": "^25.5.0", + "tsx": "^4.20.6", + "typescript": "^6.0.2" + }, + "files": [ + "bin/", + "src/", + "tsconfig.json", + "README.md", + "ARCHITECTURE.md", + "LICENSE" + ] +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 1e0e83e..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! OpenVCS Plugin Template -//! -//! A minimal plugin that demonstrates the plugin ABI structure. - -use openvcs_core::prelude::*; - -// Internal helpers - NOT ABI -#[allow(dead_code)] -/// Returns a sample helper string used by the template. -fn helper() -> String { - "Hello from internal helper!".to_string() -} - -// Plugin ABI - functions in mod plugin are exported -#[openvcs_plugin] -mod plugin { - use super::*; - - /// Initializes the template plugin. - /// - /// # Returns - /// - `Ok(())` when plugin startup succeeds. - pub fn init() -> Result<(), PluginError> { - info!("Hello, World!"); - Ok(()) - } - - /// Deinitializes the template plugin. - /// - /// # Returns - /// - `Ok(())` when plugin shutdown succeeds. - pub fn deinit() -> Result<(), PluginError> { - Ok(()) - } -} - -// Generate WIT Guest impl -openvcs_core::export_plugin!(plugin); diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 0000000..e8a428e --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,17 @@ +// Copyright © 2025-2026 OpenVCS Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +import type { PluginModuleDefinition } from '@openvcs/sdk/runtime'; + +/** Stores the log target used by the template plugin runtime. */ +const logTarget = 'openvcs.plugin-template.plugin'; + +/** Exposes the plugin definition consumed by the OpenVCS SDK runtime. */ +export const PluginDefinition: PluginModuleDefinition = { + logTarget, +}; + +/** Starts the template plugin and emits the same hello-world message. */ +export function OnPluginStart(): void { + console.info('Hello, World from PluginTemplate!'); +} diff --git a/test/plugin.test.ts b/test/plugin.test.ts new file mode 100644 index 0000000..b45d62b --- /dev/null +++ b/test/plugin.test.ts @@ -0,0 +1,38 @@ +// Copyright © 2025-2026 OpenVCS Contributors +// SPDX-License-Identifier: GPL-3.0-or-later + +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; + +import { OnPluginStart, PluginDefinition } from '../src/plugin.js'; + +/** Captures console info output while running the plugin startup hook. */ +function captureConsoleInfo(action: () => void): string[] { + const messages: string[] = []; + const originalInfo = console.info; + console.info = ((...args: unknown[]) => { + messages.push(args.map((value) => String(value)).join(' ')); + }) as typeof console.info; + + try { + action(); + } finally { + console.info = originalInfo; + } + + return messages; +} + +describe('PluginTemplate', () => { + it('exports the expected plugin definition', () => { + assert.strictEqual(PluginDefinition.logTarget, 'openvcs.plugin-template.plugin'); + }); + + it('logs the hello-world message on startup', () => { + const messages = captureConsoleInfo(() => { + OnPluginStart(); + }); + + assert.deepStrictEqual(messages, ['Hello, World from PluginTemplate!']); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..34fa1e1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "rootDir": "src", + "outDir": "bin", + "strict": true, + "types": ["node"], + "lib": ["ES2022"], + "noEmitOnError": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true + }, + "include": ["src/**/*"] +} From 69f0654256b29dfe6be26059c3f95311a71fef31 Mon Sep 17 00:00:00 2001 From: OpenVCS Date: Sun, 26 Apr 2026 07:59:46 +0100 Subject: [PATCH 12/12] Fix CI --- .github/workflows/ci.yml | 44 ++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 285076c..efad7ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,31 +3,45 @@ name: Plugin Template CI on: push: pull_request: + workflow_dispatch: + +concurrency: + group: template-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read jobs: - build: - runs-on: ubuntu-latest + node: + runs-on: ubuntu-24.04 + timeout-minutes: 10 steps: - name: Check out repository uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + - name: Set up Node + uses: actions/setup-node@v4 with: - targets: wasm32-wasip1 + node-version: 24 + cache: npm - - name: Format - working-directory: PluginTemplate - run: cargo fmt --all -- --check + - name: Install dependencies + run: npm ci - name: Lint - working-directory: PluginTemplate - run: cargo clippy --all-targets -- -D warnings + run: npm run lint - name: Test - working-directory: PluginTemplate - run: cargo test + run: npm test + + - name: Build + run: npm run build + + - name: Pack plugin + run: | + mkdir -p dist + npm pack --pack-destination dist - - name: Build WASI module - working-directory: PluginTemplate - run: cargo build --target wasm32-wasip1 --bin hello-world-plugin + - name: Verify package + run: test -n "$(ls dist/*.tgz)"