@openvcs/sdk 0.4.0 → 0.4.1-beta.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -6
- package/lib/build.d.ts +5 -8
- package/lib/build.js +42 -25
- package/lib/init.d.ts +4 -0
- package/lib/init.js +5 -14
- package/lib/npm-runner.d.ts +11 -0
- package/lib/npm-runner.js +62 -0
- package/lib/runtime/dispatcher.js +19 -11
- package/lib/runtime/vcs-delegate-base.d.ts +3 -1
- package/lib/runtime/vcs-delegate-base.js +4 -0
- package/lib/runtime/vcs-delegate-metadata.d.ts +2 -0
- package/lib/runtime/vcs-delegate-metadata.js +1 -0
- package/lib/types/vcs.d.ts +34 -10
- package/package.json +6 -3
- package/src/lib/build.ts +52 -25
- package/src/lib/init.ts +6 -17
- package/src/lib/npm-runner.ts +47 -0
- package/src/lib/runtime/dispatcher.ts +19 -11
- package/src/lib/runtime/vcs-delegate-base.ts +9 -1
- package/src/lib/runtime/vcs-delegate-metadata.ts +2 -0
- package/src/lib/types/vcs.ts +40 -11
- package/test/build.test.js +153 -2
- package/test/cli-direct.test.js +57 -0
- package/test/dispatcher.test.js +104 -0
- package/test/fs-utils.test.js +57 -0
- package/test/host.test.js +39 -0
- package/test/init.test.js +209 -1
- package/test/menu.test.js +147 -0
- package/test/modal.test.js +74 -0
- package/test/npm-runner.test.js +89 -0
- package/test/runtime.test.js +143 -0
- package/test/transport.test.js +71 -0
- package/test/vcs-delegate-base.test.js +87 -0
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
OpenVCS SDK for npm-based plugin development.
|
|
7
7
|
|
|
8
|
+
Requires Node.js 20 or newer.
|
|
9
|
+
|
|
8
10
|
Install this package in plugin projects, scaffold a starter plugin, and build
|
|
9
11
|
plugin runtime assets. The SDK also exports a Node-only JSON-RPC runtime
|
|
10
12
|
layer and shared protocol/types so plugins do not have to hand-roll stdio
|
|
@@ -41,6 +43,16 @@ Run tests (builds first):
|
|
|
41
43
|
npm test
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
Generate a coverage report:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm run coverage
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This runs the full test flow, writes c8 output to `coverage/`, and fails if
|
|
53
|
+
coverage drops below 95% lines, 95% functions, 85% branches, or 95%
|
|
54
|
+
statements.
|
|
55
|
+
|
|
44
56
|
Run the local CLI through npm scripts:
|
|
45
57
|
|
|
46
58
|
```bash
|
|
@@ -162,6 +174,9 @@ compiled plugin author module at `bin/plugin.js`, and then generates the SDK-own
|
|
|
162
174
|
`bin/<module.exec>` bootstrap that imports `./plugin.js`, applies `PluginDefinition`,
|
|
163
175
|
invokes `OnPluginStart()`, and starts the runtime.
|
|
164
176
|
|
|
177
|
+
The build prints progress messages to stderr so you can see each phase. Pass `-V`
|
|
178
|
+
or `--verbose` to also echo the exact command and validation details.
|
|
179
|
+
|
|
165
180
|
Theme-only plugins can also run `npm run build`; the command exits successfully
|
|
166
181
|
without producing `bin/` output.
|
|
167
182
|
|
|
@@ -196,18 +211,18 @@ Dependency behavior:
|
|
|
196
211
|
|
|
197
212
|
## CLI usage
|
|
198
213
|
|
|
199
|
-
Build a plugin
|
|
214
|
+
Build plugin assets from a plugin project:
|
|
200
215
|
|
|
201
216
|
```bash
|
|
202
|
-
|
|
217
|
+
npm run build
|
|
203
218
|
```
|
|
204
219
|
|
|
205
|
-
Show command help:
|
|
220
|
+
Show command help through npm script:
|
|
206
221
|
|
|
207
222
|
```bash
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
223
|
+
npm run openvcs -- --help
|
|
224
|
+
npm run openvcs -- build --help
|
|
225
|
+
npm run openvcs -- init --help
|
|
211
226
|
```
|
|
212
227
|
|
|
213
228
|
## Publishing Note
|
package/lib/build.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { npmExecutable } from "./npm-runner";
|
|
1
2
|
/** CLI arguments for `openvcs build`. */
|
|
2
3
|
export interface BuildArgs {
|
|
3
4
|
pluginDir: string;
|
|
@@ -10,26 +11,22 @@ export interface ManifestInfo {
|
|
|
10
11
|
entry: string | undefined;
|
|
11
12
|
manifestPath: string;
|
|
12
13
|
}
|
|
13
|
-
/** Returns the npm executable name for the current platform. */
|
|
14
|
-
export declare function npmExecutable(): string;
|
|
15
|
-
/** Returns whether a command must be launched via the Windows shell. */
|
|
16
|
-
export declare function shouldUseWindowsShell(program: string): boolean;
|
|
17
14
|
/** Formats help text for the build command. */
|
|
18
15
|
export declare function buildUsage(commandName?: string): string;
|
|
19
16
|
/** Parses `openvcs build` arguments. */
|
|
20
17
|
export declare function parseBuildArgs(args: string[]): BuildArgs;
|
|
21
18
|
/** Reads and validates the plugin manifest. */
|
|
22
|
-
export declare function readManifest(pluginDir: string): ManifestInfo;
|
|
19
|
+
export declare function readManifest(pluginDir: string, verbose?: boolean): ManifestInfo;
|
|
23
20
|
/** Verifies that a declared module entry resolves to a real file under `bin/`. */
|
|
24
|
-
export declare function validateDeclaredModuleExec(pluginDir: string, moduleExec: string | undefined): void;
|
|
21
|
+
export declare function validateDeclaredModuleExec(pluginDir: string, moduleExec: string | undefined, verbose?: boolean): void;
|
|
25
22
|
/** Returns the compiled plugin module path imported by the generated bootstrap. */
|
|
26
23
|
export declare function authoredPluginModulePath(pluginDir: string): string;
|
|
27
24
|
/** Ensures the plugin's authored module and generated bootstrap paths are compatible. */
|
|
28
|
-
export declare function validateGeneratedBootstrapTargets(pluginDir: string, moduleExec: string | undefined): void;
|
|
25
|
+
export declare function validateGeneratedBootstrapTargets(pluginDir: string, moduleExec: string | undefined, verbose?: boolean): void;
|
|
29
26
|
/** Renders the generated Node bootstrap that owns runtime startup. */
|
|
30
27
|
export declare function renderGeneratedBootstrap(pluginModuleImportPath: string, isEsm: boolean): string;
|
|
31
28
|
/** Writes the generated SDK-owned module entrypoint under `bin/<module.exec>`. */
|
|
32
|
-
export declare function generateModuleBootstrap(pluginDir: string, moduleExec: string | undefined): void;
|
|
29
|
+
export declare function generateModuleBootstrap(pluginDir: string, moduleExec: string | undefined, verbose?: boolean): void;
|
|
33
30
|
/** Returns whether the plugin repository has a `package.json`. */
|
|
34
31
|
export declare function hasPackageJson(pluginDir: string): boolean;
|
|
35
32
|
/** Runs a command in the given directory with optional verbose logging. */
|
package/lib/build.js
CHANGED
|
@@ -35,8 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
};
|
|
36
36
|
})();
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.npmExecutable =
|
|
39
|
-
exports.shouldUseWindowsShell = shouldUseWindowsShell;
|
|
38
|
+
exports.npmExecutable = void 0;
|
|
40
39
|
exports.buildUsage = buildUsage;
|
|
41
40
|
exports.parseBuildArgs = parseBuildArgs;
|
|
42
41
|
exports.readManifest = readManifest;
|
|
@@ -52,19 +51,13 @@ const fs = __importStar(require("node:fs"));
|
|
|
52
51
|
const path = __importStar(require("node:path"));
|
|
53
52
|
const node_child_process_1 = require("node:child_process");
|
|
54
53
|
const fs_utils_1 = require("./fs-utils");
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
/** Returns whether a command must be launched via the Windows shell. */
|
|
61
|
-
function shouldUseWindowsShell(program) {
|
|
62
|
-
if (process.platform !== "win32") {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
const normalized = program.toLowerCase();
|
|
66
|
-
return normalized === "npm" || normalized.endsWith(".cmd") || normalized.endsWith(".bat");
|
|
54
|
+
const npm_runner_1 = require("./npm-runner");
|
|
55
|
+
var npm_runner_2 = require("./npm-runner");
|
|
56
|
+
Object.defineProperty(exports, "npmExecutable", { enumerable: true, get: function () { return npm_runner_2.npmExecutable; } });
|
|
57
|
+
function writeBuildProgress(message) {
|
|
58
|
+
process.stderr.write(`openvcs build: ${message}\n`);
|
|
67
59
|
}
|
|
60
|
+
const AUTHORED_PLUGIN_MODULE_BASENAME = "plugin.js";
|
|
68
61
|
/** Formats help text for the build command. */
|
|
69
62
|
function buildUsage(commandName = "openvcs") {
|
|
70
63
|
return `${commandName} build [args]\n\n --plugin-dir <path> Plugin repository root (contains package.json with openvcs metadata)\n -V, --verbose Enable verbose output\n`;
|
|
@@ -100,11 +93,14 @@ function parseBuildArgs(args) {
|
|
|
100
93
|
};
|
|
101
94
|
}
|
|
102
95
|
/** Reads and validates the plugin manifest. */
|
|
103
|
-
function readManifest(pluginDir) {
|
|
96
|
+
function readManifest(pluginDir, verbose = false) {
|
|
104
97
|
const manifestPath = path.join(pluginDir, "package.json");
|
|
105
98
|
let manifestRaw;
|
|
106
99
|
let manifestFd;
|
|
107
100
|
let manifest;
|
|
101
|
+
if (verbose) {
|
|
102
|
+
writeBuildProgress(`reading manifest at ${manifestPath}`);
|
|
103
|
+
}
|
|
108
104
|
try {
|
|
109
105
|
manifestFd = fs.openSync(manifestPath, "r");
|
|
110
106
|
const manifestStat = fs.fstatSync(manifestFd);
|
|
@@ -148,6 +144,9 @@ function readManifest(pluginDir) {
|
|
|
148
144
|
const moduleExec = typeof moduleValue?.exec === "string" ? moduleValue.exec.trim() : undefined;
|
|
149
145
|
const entryValue = openvcs.entry;
|
|
150
146
|
const entry = typeof entryValue === "string" ? entryValue.trim() : undefined;
|
|
147
|
+
if (verbose) {
|
|
148
|
+
writeBuildProgress(`manifest loaded for ${pluginId}${moduleExec ? ` (module.exec: ${moduleExec})` : ""}`);
|
|
149
|
+
}
|
|
151
150
|
return {
|
|
152
151
|
pluginId,
|
|
153
152
|
moduleExec,
|
|
@@ -156,11 +155,14 @@ function readManifest(pluginDir) {
|
|
|
156
155
|
};
|
|
157
156
|
}
|
|
158
157
|
/** Verifies that a declared module entry resolves to a real file under `bin/`. */
|
|
159
|
-
function validateDeclaredModuleExec(pluginDir, moduleExec) {
|
|
158
|
+
function validateDeclaredModuleExec(pluginDir, moduleExec, verbose = false) {
|
|
160
159
|
if (!moduleExec) {
|
|
161
160
|
return;
|
|
162
161
|
}
|
|
163
162
|
const targetPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
163
|
+
if (verbose) {
|
|
164
|
+
writeBuildProgress(`checking declared bootstrap at ${targetPath}`);
|
|
165
|
+
}
|
|
164
166
|
if (!fs.existsSync(targetPath) || !fs.lstatSync(targetPath).isFile()) {
|
|
165
167
|
throw new Error(`module entrypoint not found at ${targetPath}`);
|
|
166
168
|
}
|
|
@@ -187,10 +189,13 @@ function authoredPluginModulePath(pluginDir) {
|
|
|
187
189
|
return path.resolve(pluginDir, "bin", AUTHORED_PLUGIN_MODULE_BASENAME);
|
|
188
190
|
}
|
|
189
191
|
/** Ensures the plugin's authored module and generated bootstrap paths are compatible. */
|
|
190
|
-
function validateGeneratedBootstrapTargets(pluginDir, moduleExec) {
|
|
192
|
+
function validateGeneratedBootstrapTargets(pluginDir, moduleExec, verbose = false) {
|
|
191
193
|
if (!moduleExec) {
|
|
192
194
|
return;
|
|
193
195
|
}
|
|
196
|
+
if (verbose) {
|
|
197
|
+
writeBuildProgress(`validating generated bootstrap target for ${moduleExec}`);
|
|
198
|
+
}
|
|
194
199
|
resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
195
200
|
const normalizedExec = moduleExec.trim().toLowerCase();
|
|
196
201
|
if (normalizedExec === AUTHORED_PLUGIN_MODULE_BASENAME.toLowerCase()) {
|
|
@@ -221,16 +226,21 @@ function renderGeneratedBootstrap(pluginModuleImportPath, isEsm) {
|
|
|
221
226
|
return `#!/usr/bin/env node\n// Copyright © 2025-2026 OpenVCS Contributors\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n(async () => {\n const { bootstrapPluginModule } = require('@openvcs/sdk/runtime');\n await bootstrapPluginModule({\n importPluginModule: async () => require('${pluginModuleImportPath}'),\n modulePath: '${pluginModuleImportPath}',\n });\n})();\n`;
|
|
222
227
|
}
|
|
223
228
|
/** Writes the generated SDK-owned module entrypoint under `bin/<module.exec>`. */
|
|
224
|
-
function generateModuleBootstrap(pluginDir, moduleExec) {
|
|
229
|
+
function generateModuleBootstrap(pluginDir, moduleExec, verbose = false) {
|
|
225
230
|
if (!moduleExec) {
|
|
226
|
-
|
|
231
|
+
if (verbose) {
|
|
232
|
+
writeBuildProgress(`no module.exec defined for ${pluginDir}; skipping bootstrap generation`);
|
|
233
|
+
}
|
|
227
234
|
return;
|
|
228
235
|
}
|
|
229
|
-
validateGeneratedBootstrapTargets(pluginDir, moduleExec);
|
|
236
|
+
validateGeneratedBootstrapTargets(pluginDir, moduleExec, verbose);
|
|
230
237
|
const execPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
|
|
231
238
|
const pluginModulePath = authoredPluginModulePath(pluginDir);
|
|
232
239
|
const pluginModuleImportPath = relativeBinImport(execPath, pluginModulePath);
|
|
233
240
|
const isEsm = detectEsmMode(pluginDir, moduleExec);
|
|
241
|
+
if (verbose) {
|
|
242
|
+
writeBuildProgress(`writing bootstrap ${execPath} -> ${pluginModuleImportPath}${isEsm ? " (esm)" : " (cjs)"}`);
|
|
243
|
+
}
|
|
234
244
|
fs.mkdirSync(path.dirname(execPath), { recursive: true });
|
|
235
245
|
fs.writeFileSync(execPath, renderGeneratedBootstrap(pluginModuleImportPath, isEsm), "utf8");
|
|
236
246
|
}
|
|
@@ -262,8 +272,8 @@ function runCommand(program, args, cwd, verbose) {
|
|
|
262
272
|
}
|
|
263
273
|
const result = (0, node_child_process_1.spawnSync)(program, args, {
|
|
264
274
|
cwd,
|
|
265
|
-
shell: shouldUseWindowsShell(program),
|
|
266
275
|
stdio: ["ignore", verbose ? "inherit" : "ignore", "inherit"],
|
|
276
|
+
windowsHide: true,
|
|
267
277
|
});
|
|
268
278
|
if (result.error) {
|
|
269
279
|
throw new Error(`failed to spawn '${program}' in ${cwd}: ${result.error.message}`);
|
|
@@ -295,10 +305,13 @@ function readPackageScripts(pluginDir) {
|
|
|
295
305
|
}
|
|
296
306
|
/** Builds a plugin's runtime assets when it declares a code module. */
|
|
297
307
|
function buildPluginAssets(parsedArgs) {
|
|
298
|
-
|
|
308
|
+
writeBuildProgress(`reading plugin manifest in ${parsedArgs.pluginDir}`);
|
|
309
|
+
const manifest = readManifest(parsedArgs.pluginDir, parsedArgs.verbose);
|
|
299
310
|
if (!manifest.moduleExec) {
|
|
311
|
+
writeBuildProgress(`theme-only plugin ${manifest.pluginId}; nothing to build`);
|
|
300
312
|
return manifest;
|
|
301
313
|
}
|
|
314
|
+
writeBuildProgress(`building plugin ${manifest.pluginId}`);
|
|
302
315
|
if (!hasPackageJson(parsedArgs.pluginDir)) {
|
|
303
316
|
throw new Error(`code plugins must include package.json: ${path.join(parsedArgs.pluginDir, "package.json")}`);
|
|
304
317
|
}
|
|
@@ -307,8 +320,12 @@ function buildPluginAssets(parsedArgs) {
|
|
|
307
320
|
if (typeof buildScript !== "string" || buildScript.trim() === "") {
|
|
308
321
|
throw new Error(`code plugins must define scripts[\"build:plugin\"] in ${path.join(parsedArgs.pluginDir, "package.json")}`);
|
|
309
322
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
writeBuildProgress(`running build:plugin`);
|
|
324
|
+
runCommand((0, npm_runner_1.npmExecutable)(), [...(0, npm_runner_1.npmArgsPrefix)(), "run", "build:plugin"], parsedArgs.pluginDir, parsedArgs.verbose);
|
|
325
|
+
writeBuildProgress(`generating bootstrap ${manifest.moduleExec}`);
|
|
326
|
+
generateModuleBootstrap(parsedArgs.pluginDir, manifest.moduleExec, parsedArgs.verbose);
|
|
327
|
+
writeBuildProgress(`validating bootstrap ${manifest.moduleExec}`);
|
|
328
|
+
validateDeclaredModuleExec(parsedArgs.pluginDir, manifest.moduleExec, parsedArgs.verbose);
|
|
329
|
+
writeBuildProgress(`build complete for ${manifest.pluginId}`);
|
|
313
330
|
return manifest;
|
|
314
331
|
}
|
package/lib/init.d.ts
CHANGED
|
@@ -22,18 +22,22 @@ interface InitCommandError {
|
|
|
22
22
|
export declare function initUsage(commandName?: string): string;
|
|
23
23
|
declare function sanitizeIdToken(raw: string): string;
|
|
24
24
|
declare function defaultPluginIdFromDir(targetDir: string): string;
|
|
25
|
+
declare function defaultPluginNameFromId(pluginId: string): string;
|
|
25
26
|
declare function validatePluginId(pluginId: string): string | undefined;
|
|
26
27
|
declare function createReadlinePromptDriver(output?: NodeJS.WritableStream): PromptDriver;
|
|
27
28
|
declare function collectAnswers({ forceTheme, targetHint }: CollectAnswersOptions, promptDriver?: PromptDriver, output?: NodeJS.WritableStream): Promise<InitAnswers>;
|
|
28
29
|
declare function writeModuleTemplate(answers: InitAnswers): void;
|
|
30
|
+
declare function writeThemeTemplate(answers: InitAnswers): void;
|
|
29
31
|
export declare function runInitCommand(args: string[]): Promise<string>;
|
|
30
32
|
export declare function isUsageError(error: unknown): error is InitCommandError;
|
|
31
33
|
export declare const __private: {
|
|
32
34
|
collectAnswers: typeof collectAnswers;
|
|
33
35
|
createReadlinePromptDriver: typeof createReadlinePromptDriver;
|
|
36
|
+
defaultPluginNameFromId: typeof defaultPluginNameFromId;
|
|
34
37
|
defaultPluginIdFromDir: typeof defaultPluginIdFromDir;
|
|
35
38
|
sanitizeIdToken: typeof sanitizeIdToken;
|
|
36
39
|
validatePluginId: typeof validatePluginId;
|
|
37
40
|
writeModuleTemplate: typeof writeModuleTemplate;
|
|
41
|
+
writeThemeTemplate: typeof writeThemeTemplate;
|
|
38
42
|
};
|
|
39
43
|
export {};
|
package/lib/init.js
CHANGED
|
@@ -42,17 +42,8 @@ const path = __importStar(require("node:path"));
|
|
|
42
42
|
const readline = __importStar(require("node:readline/promises"));
|
|
43
43
|
const node_process_1 = require("node:process");
|
|
44
44
|
const node_child_process_1 = require("node:child_process");
|
|
45
|
+
const npm_runner_1 = require("./npm-runner");
|
|
45
46
|
const packageJson = require("../package.json");
|
|
46
|
-
function npmExecutable() {
|
|
47
|
-
return "npm";
|
|
48
|
-
}
|
|
49
|
-
function shouldUseWindowsShell(program) {
|
|
50
|
-
if (process.platform !== "win32") {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
const normalized = program.toLowerCase();
|
|
54
|
-
return normalized === "npm" || normalized.endsWith(".cmd") || normalized.endsWith(".bat");
|
|
55
|
-
}
|
|
56
47
|
function initUsage(commandName = "openvcs") {
|
|
57
48
|
return `Usage: ${commandName} init [--theme] [target-dir]\n\nOptions:\n --theme Start with a theme-only plugin template\n`;
|
|
58
49
|
}
|
|
@@ -203,10 +194,10 @@ async function collectAnswers({ forceTheme, targetHint }, promptDriver = createR
|
|
|
203
194
|
}
|
|
204
195
|
}
|
|
205
196
|
function runNpmInstall(targetDir) {
|
|
206
|
-
const result = (0, node_child_process_1.spawnSync)(npmExecutable(), ["install"], {
|
|
197
|
+
const result = (0, node_child_process_1.spawnSync)((0, npm_runner_1.npmExecutable)(), [...(0, npm_runner_1.npmArgsPrefix)(), "install"], {
|
|
207
198
|
cwd: targetDir,
|
|
208
|
-
shell: shouldUseWindowsShell(npmExecutable()),
|
|
209
199
|
stdio: "inherit",
|
|
200
|
+
windowsHide: true,
|
|
210
201
|
});
|
|
211
202
|
if (result.error) {
|
|
212
203
|
throw new Error(`failed to spawn npm install in ${targetDir}: ${result.error.message}`);
|
|
@@ -228,7 +219,6 @@ function writeModuleTemplate(answers) {
|
|
|
228
219
|
openvcs: {
|
|
229
220
|
id: answers.pluginId,
|
|
230
221
|
name: answers.pluginName,
|
|
231
|
-
version: answers.pluginVersion,
|
|
232
222
|
default_enabled: answers.defaultEnabled,
|
|
233
223
|
module: { exec: "openvcs-plugin.js" },
|
|
234
224
|
},
|
|
@@ -269,7 +259,6 @@ function writeThemeTemplate(answers) {
|
|
|
269
259
|
openvcs: {
|
|
270
260
|
id: answers.pluginId,
|
|
271
261
|
name: answers.pluginName,
|
|
272
|
-
version: answers.pluginVersion,
|
|
273
262
|
default_enabled: answers.defaultEnabled,
|
|
274
263
|
},
|
|
275
264
|
scripts: {
|
|
@@ -350,8 +339,10 @@ function isUsageError(error) {
|
|
|
350
339
|
exports.__private = {
|
|
351
340
|
collectAnswers,
|
|
352
341
|
createReadlinePromptDriver,
|
|
342
|
+
defaultPluginNameFromId,
|
|
353
343
|
defaultPluginIdFromDir,
|
|
354
344
|
sanitizeIdToken,
|
|
355
345
|
validatePluginId,
|
|
356
346
|
writeModuleTemplate,
|
|
347
|
+
writeThemeTemplate,
|
|
357
348
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type ExistsFn = (path: string) => boolean;
|
|
2
|
+
type ResolveFn = (specifier: string) => string;
|
|
3
|
+
export interface NpmCommand {
|
|
4
|
+
program: string;
|
|
5
|
+
argsPrefix: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveNpmCli(execPath?: string, exists?: ExistsFn, resolve?: ResolveFn): string;
|
|
8
|
+
export declare function npmCommand(platform?: NodeJS.Platform, execPath?: string, exists?: ExistsFn, resolve?: ResolveFn): NpmCommand;
|
|
9
|
+
export declare function npmExecutable(): string;
|
|
10
|
+
export declare function npmArgsPrefix(): string[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright © 2025-2026 OpenVCS Contributors
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
+
var ownKeys = function(o) {
|
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
+
var ar = [];
|
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
+
return ar;
|
|
26
|
+
};
|
|
27
|
+
return ownKeys(o);
|
|
28
|
+
};
|
|
29
|
+
return function (mod) {
|
|
30
|
+
if (mod && mod.__esModule) return mod;
|
|
31
|
+
var result = {};
|
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
+
__setModuleDefault(result, mod);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.resolveNpmCli = resolveNpmCli;
|
|
39
|
+
exports.npmCommand = npmCommand;
|
|
40
|
+
exports.npmExecutable = npmExecutable;
|
|
41
|
+
exports.npmArgsPrefix = npmArgsPrefix;
|
|
42
|
+
const fs = __importStar(require("node:fs"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
function resolveNpmCli(execPath = process.execPath, exists = fs.existsSync, resolve = require.resolve) {
|
|
45
|
+
const localNodeModules = path.join(path.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
46
|
+
if (exists(localNodeModules)) {
|
|
47
|
+
return localNodeModules;
|
|
48
|
+
}
|
|
49
|
+
return resolve("npm/bin/npm-cli.js");
|
|
50
|
+
}
|
|
51
|
+
function npmCommand(platform = process.platform, execPath = process.execPath, exists = fs.existsSync, resolve = require.resolve) {
|
|
52
|
+
if (platform !== "win32") {
|
|
53
|
+
return { program: "npm", argsPrefix: [] };
|
|
54
|
+
}
|
|
55
|
+
return { program: execPath, argsPrefix: [resolveNpmCli(execPath, exists, resolve)] };
|
|
56
|
+
}
|
|
57
|
+
function npmExecutable() {
|
|
58
|
+
return npmCommand().program;
|
|
59
|
+
}
|
|
60
|
+
function npmArgsPrefix() {
|
|
61
|
+
return npmCommand().argsPrefix;
|
|
62
|
+
}
|
|
@@ -83,23 +83,31 @@ function createRuntimeDispatcher(options, host, writer) {
|
|
|
83
83
|
}
|
|
84
84
|
let result;
|
|
85
85
|
if (timeout && timeout > 0) {
|
|
86
|
-
|
|
87
|
-
const
|
|
86
|
+
let timeoutId;
|
|
87
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
88
|
+
timeoutId = setTimeout(() => {
|
|
89
|
+
reject((0, errors_1.pluginError)('request-timeout', `method '${method}' timed out after ${timeout}ms`));
|
|
90
|
+
}, timeout);
|
|
91
|
+
});
|
|
88
92
|
try {
|
|
89
|
-
result = await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
result = await Promise.race([
|
|
94
|
+
handler(params, {
|
|
95
|
+
host,
|
|
96
|
+
requestId: id,
|
|
97
|
+
method,
|
|
98
|
+
}),
|
|
99
|
+
timeoutPromise,
|
|
100
|
+
]);
|
|
94
101
|
}
|
|
95
102
|
catch (error) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
throw (0, errors_1.pluginError)('request-timeout', `method '${method}' timed out after ${timeout}ms`);
|
|
103
|
+
if (timeoutId !== undefined) {
|
|
104
|
+
clearTimeout(timeoutId);
|
|
99
105
|
}
|
|
100
106
|
throw error;
|
|
101
107
|
}
|
|
102
|
-
|
|
108
|
+
if (timeoutId !== undefined) {
|
|
109
|
+
clearTimeout(timeoutId);
|
|
110
|
+
}
|
|
103
111
|
}
|
|
104
112
|
else {
|
|
105
113
|
result = await handler(params, {
|
|
@@ -66,7 +66,7 @@ export declare abstract class VcsDelegateBase<TDeps, TContext = PluginRuntimeCon
|
|
|
66
66
|
/** Handles `vcs.list_commits`. */
|
|
67
67
|
listCommits(_params: VcsTypes.VcsListCommitsParams, _context: TContext): VcsHandlerResult<VcsTypes.CommitEntry[]>;
|
|
68
68
|
/** Handles `vcs.diff_file`. */
|
|
69
|
-
diffFile(_params: VcsTypes.VcsDiffFileParams, _context: TContext): VcsHandlerResult<
|
|
69
|
+
diffFile(_params: VcsTypes.VcsDiffFileParams, _context: TContext): VcsHandlerResult<VcsTypes.VcsDiffFileResponse>;
|
|
70
70
|
/** Handles `vcs.diff_commit`. */
|
|
71
71
|
diffCommit(_params: VcsTypes.VcsDiffCommitParams, _context: TContext): VcsHandlerResult<string[]>;
|
|
72
72
|
/** Handles `vcs.get_conflict_details`. */
|
|
@@ -77,6 +77,8 @@ export declare abstract class VcsDelegateBase<TDeps, TContext = PluginRuntimeCon
|
|
|
77
77
|
writeMergeResult(_params: VcsTypes.VcsWriteMergeResultParams, _context: TContext): VcsHandlerResult<null>;
|
|
78
78
|
/** Handles `vcs.stage_patch`. */
|
|
79
79
|
stagePatch(_params: VcsTypes.VcsStagePatchParams, _context: TContext): VcsHandlerResult<null>;
|
|
80
|
+
/** Handles `vcs.stage_selections` (structured hunk/line selections). */
|
|
81
|
+
stageSelections(_params: VcsTypes.VcsStageSelectionsParams, _context: TContext): VcsHandlerResult<null>;
|
|
80
82
|
/** Handles `vcs.stage_paths`. */
|
|
81
83
|
stagePaths(_params: VcsTypes.VcsStagePathsParams, _context: TContext): VcsHandlerResult<null>;
|
|
82
84
|
/** Handles `vcs.discard_paths`. */
|
|
@@ -157,6 +157,10 @@ class VcsDelegateBase {
|
|
|
157
157
|
stagePatch(_params, _context) {
|
|
158
158
|
return this.unimplemented('stagePatch');
|
|
159
159
|
}
|
|
160
|
+
/** Handles `vcs.stage_selections` (structured hunk/line selections). */
|
|
161
|
+
stageSelections(_params, _context) {
|
|
162
|
+
return this.unimplemented('stageSelections');
|
|
163
|
+
}
|
|
160
164
|
/** Handles `vcs.stage_paths`. */
|
|
161
165
|
stagePaths(_params, _context) {
|
|
162
166
|
return this.unimplemented('stagePaths');
|
|
@@ -30,6 +30,7 @@ export type VcsDelegateBindings<TContext> = {
|
|
|
30
30
|
checkoutConflictSide: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.checkout_conflict_side']>;
|
|
31
31
|
writeMergeResult: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.write_merge_result']>;
|
|
32
32
|
stagePatch: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_patch']>;
|
|
33
|
+
stageSelections: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_selections']>;
|
|
33
34
|
stagePaths: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.stage_paths']>;
|
|
34
35
|
discardPaths: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.discard_paths']>;
|
|
35
36
|
applyReversePatch: NonNullable<VcsTypes.VcsDelegates<TContext>['vcs.apply_reverse_patch']>;
|
|
@@ -86,6 +87,7 @@ export declare const VCS_DELEGATE_METHOD_MAPPINGS: {
|
|
|
86
87
|
readonly checkoutConflictSide: "vcs.checkout_conflict_side";
|
|
87
88
|
readonly writeMergeResult: "vcs.write_merge_result";
|
|
88
89
|
readonly stagePatch: "vcs.stage_patch";
|
|
90
|
+
readonly stageSelections: "vcs.stage_selections";
|
|
89
91
|
readonly stagePaths: "vcs.stage_paths";
|
|
90
92
|
readonly discardPaths: "vcs.discard_paths";
|
|
91
93
|
readonly applyReversePatch: "vcs.apply_reverse_patch";
|
|
@@ -33,6 +33,7 @@ exports.VCS_DELEGATE_METHOD_MAPPINGS = {
|
|
|
33
33
|
checkoutConflictSide: 'vcs.checkout_conflict_side',
|
|
34
34
|
writeMergeResult: 'vcs.write_merge_result',
|
|
35
35
|
stagePatch: 'vcs.stage_patch',
|
|
36
|
+
stageSelections: 'vcs.stage_selections',
|
|
36
37
|
stagePaths: 'vcs.stage_paths',
|
|
37
38
|
discardPaths: 'vcs.discard_paths',
|
|
38
39
|
applyReversePatch: 'vcs.apply_reverse_patch',
|
package/lib/types/vcs.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface VcsCapabilities {
|
|
|
13
13
|
push_pull: boolean;
|
|
14
14
|
/** Indicates whether fast-forward helpers are supported. */
|
|
15
15
|
fast_forward: boolean;
|
|
16
|
+
/** Merge strategies the backend supports (values like "merge", "squash", "rebase"). */
|
|
17
|
+
merge_strategies?: string[];
|
|
16
18
|
}
|
|
17
19
|
/** Describes params that carry a repository session id. */
|
|
18
20
|
export interface VcsSessionParams extends RequestParams {
|
|
@@ -92,19 +94,12 @@ export interface VcsRemoveRemoteParams extends VcsSessionParams {
|
|
|
92
94
|
/** Stores the remote name to remove. */
|
|
93
95
|
name: string;
|
|
94
96
|
}
|
|
95
|
-
/** Describes optional fetch flags. */
|
|
96
|
-
export interface VcsFetchOptions {
|
|
97
|
-
/** Indicates whether stale remote references should be pruned. */
|
|
98
|
-
prune?: boolean;
|
|
99
|
-
}
|
|
100
97
|
/** Describes params for fetch methods. */
|
|
101
98
|
export interface VcsFetchParams extends VcsSessionParams {
|
|
102
99
|
/** Stores the remote to fetch when one is supplied. */
|
|
103
100
|
remote?: string;
|
|
104
101
|
/** Stores the refspec to fetch when one is supplied. */
|
|
105
102
|
refspec?: string;
|
|
106
|
-
/** Stores optional fetch flags. */
|
|
107
|
-
opts?: VcsFetchOptions;
|
|
108
103
|
}
|
|
109
104
|
/** Describes params for push. */
|
|
110
105
|
export interface VcsPushParams extends VcsSessionParams {
|
|
@@ -156,6 +151,8 @@ export interface StatusFileEntry {
|
|
|
156
151
|
resolved_conflict: boolean;
|
|
157
152
|
/** Stores placeholder hunk information until richer diff support exists. */
|
|
158
153
|
hunks: never[];
|
|
154
|
+
/** Indicates whether the file content should be treated as binary. */
|
|
155
|
+
binary?: boolean | null;
|
|
159
156
|
}
|
|
160
157
|
/** Describes the structured status payload returned to the host. */
|
|
161
158
|
export interface StatusPayload {
|
|
@@ -224,6 +221,15 @@ export interface VcsDiffCommitParams extends VcsSessionParams {
|
|
|
224
221
|
/** Stores the revision to diff. */
|
|
225
222
|
rev: string;
|
|
226
223
|
}
|
|
224
|
+
/** Describes one structured diff payload returned for a file. */
|
|
225
|
+
export interface VcsDiffResult {
|
|
226
|
+
/** Stores line-oriented diff output. */
|
|
227
|
+
lines: string[];
|
|
228
|
+
/** Indicates whether the diff target should be treated as binary. */
|
|
229
|
+
binary?: boolean | null;
|
|
230
|
+
}
|
|
231
|
+
/** Describes the accepted `vcs.diff_file` response shapes. */
|
|
232
|
+
export type VcsDiffFileResponse = VcsDiffResult | string[];
|
|
227
233
|
/** Describes params for `vcs.get_conflict_details`. */
|
|
228
234
|
export interface VcsGetConflictDetailsParams extends VcsSessionParams {
|
|
229
235
|
/** Stores the conflicted path. */
|
|
@@ -241,8 +247,6 @@ export interface VcsConflictDetails {
|
|
|
241
247
|
theirs: string | null;
|
|
242
248
|
/** Indicates whether the conflict is binary. */
|
|
243
249
|
binary: boolean;
|
|
244
|
-
/** Indicates whether the conflict references Git LFS content. */
|
|
245
|
-
lfs_pointer: boolean;
|
|
246
250
|
}
|
|
247
251
|
/** Describes params for checking out one side of a conflict. */
|
|
248
252
|
export interface VcsCheckoutConflictSideParams extends VcsSessionParams {
|
|
@@ -263,6 +267,20 @@ export interface VcsStagePatchParams extends VcsSessionParams {
|
|
|
263
267
|
/** Stores the textual patch content. */
|
|
264
268
|
patch: string;
|
|
265
269
|
}
|
|
270
|
+
/** Describes a single file's hunk/line selection for partial staging. */
|
|
271
|
+
export interface HunkSelection {
|
|
272
|
+
/** Repository-relative file path. */
|
|
273
|
+
path: string;
|
|
274
|
+
/** Indices of whole hunks to include. */
|
|
275
|
+
whole_hunks: number[];
|
|
276
|
+
/** Per-hunk line selections: maps hunk index → 1-based line offsets. */
|
|
277
|
+
partial_hunks: Record<number, number[]>;
|
|
278
|
+
}
|
|
279
|
+
/** Describes params for staging structured selections (VCS-agnostic). */
|
|
280
|
+
export interface VcsStageSelectionsParams extends VcsSessionParams {
|
|
281
|
+
/** Structured hunk/line selections for multiple files. */
|
|
282
|
+
selections: HunkSelection[];
|
|
283
|
+
}
|
|
266
284
|
/** Describes params for staging repository-relative paths into the index. */
|
|
267
285
|
export interface VcsStagePathsParams extends VcsSessionParams {
|
|
268
286
|
/** Stores the paths to stage. */
|
|
@@ -292,12 +310,16 @@ export interface VcsRenameBranchParams extends VcsSessionParams {
|
|
|
292
310
|
/** Stores the next branch name. */
|
|
293
311
|
new: string;
|
|
294
312
|
}
|
|
313
|
+
/** Describes the supported merge strategies. */
|
|
314
|
+
export type VcsMergeStrategy = 'merge' | 'squash' | 'rebase';
|
|
295
315
|
/** Describes params for merging another branch into the current branch. */
|
|
296
316
|
export interface VcsMergeIntoCurrentParams extends VcsSessionParams {
|
|
297
317
|
/** Stores the branch name to merge. */
|
|
298
318
|
name: string;
|
|
299
319
|
/** Stores an optional merge commit message. */
|
|
300
320
|
message?: string;
|
|
321
|
+
/** Stores the merge strategy. Defaults to 'merge' when absent. */
|
|
322
|
+
strategy?: VcsMergeStrategy;
|
|
301
323
|
}
|
|
302
324
|
/** Describes params for setting a branch upstream. */
|
|
303
325
|
export interface VcsSetBranchUpstreamParams extends VcsSessionParams {
|
|
@@ -420,7 +442,7 @@ export interface VcsDelegates<TContext = unknown> {
|
|
|
420
442
|
/** Handles `vcs.list_commits`. */
|
|
421
443
|
'vcs.list_commits'?: RpcMethodHandler<VcsListCommitsParams, CommitEntry[], TContext>;
|
|
422
444
|
/** Handles `vcs.diff_file`. */
|
|
423
|
-
'vcs.diff_file'?: RpcMethodHandler<VcsDiffFileParams,
|
|
445
|
+
'vcs.diff_file'?: RpcMethodHandler<VcsDiffFileParams, VcsDiffFileResponse, TContext>;
|
|
424
446
|
/** Handles `vcs.diff_commit`. */
|
|
425
447
|
'vcs.diff_commit'?: RpcMethodHandler<VcsDiffCommitParams, string[], TContext>;
|
|
426
448
|
/** Handles `vcs.get_conflict_details`. */
|
|
@@ -431,6 +453,8 @@ export interface VcsDelegates<TContext = unknown> {
|
|
|
431
453
|
'vcs.write_merge_result'?: RpcMethodHandler<VcsWriteMergeResultParams, null, TContext>;
|
|
432
454
|
/** Handles `vcs.stage_patch`. */
|
|
433
455
|
'vcs.stage_patch'?: RpcMethodHandler<VcsStagePatchParams, null, TContext>;
|
|
456
|
+
/** Handles `vcs.stage_selections` (structured hunk/line selections). */
|
|
457
|
+
'vcs.stage_selections'?: RpcMethodHandler<VcsStageSelectionsParams, null, TContext>;
|
|
434
458
|
/** Handles `vcs.stage_paths`. */
|
|
435
459
|
'vcs.stage_paths'?: RpcMethodHandler<VcsStagePathsParams, null, TContext>;
|
|
436
460
|
/** Handles `vcs.discard_paths`. */
|