@openvcs/sdk 0.2.3 → 0.2.5

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.
Files changed (54) hide show
  1. package/README.md +39 -3
  2. package/lib/build.d.ts +8 -0
  3. package/lib/build.js +81 -2
  4. package/lib/dist.js +1 -0
  5. package/lib/init.d.ts +2 -0
  6. package/lib/init.js +7 -4
  7. package/lib/runtime/contracts.d.ts +45 -0
  8. package/lib/runtime/contracts.js +4 -0
  9. package/lib/runtime/dispatcher.d.ts +16 -0
  10. package/lib/runtime/dispatcher.js +133 -0
  11. package/lib/runtime/errors.d.ts +5 -0
  12. package/lib/runtime/errors.js +26 -0
  13. package/lib/runtime/factory.d.ts +3 -0
  14. package/lib/runtime/factory.js +153 -0
  15. package/lib/runtime/host.d.ts +10 -0
  16. package/lib/runtime/host.js +48 -0
  17. package/lib/runtime/index.d.ts +10 -0
  18. package/lib/runtime/index.js +23 -0
  19. package/lib/runtime/registration.d.ts +39 -0
  20. package/lib/runtime/registration.js +37 -0
  21. package/lib/runtime/transport.d.ts +14 -0
  22. package/lib/runtime/transport.js +72 -0
  23. package/lib/types/host.d.ts +57 -0
  24. package/lib/types/host.js +4 -0
  25. package/lib/types/index.d.ts +4 -0
  26. package/lib/types/index.js +22 -0
  27. package/lib/types/plugin.d.ts +56 -0
  28. package/lib/types/plugin.js +4 -0
  29. package/lib/types/protocol.d.ts +77 -0
  30. package/lib/types/protocol.js +13 -0
  31. package/lib/types/vcs.d.ts +459 -0
  32. package/lib/types/vcs.js +4 -0
  33. package/package.json +14 -1
  34. package/src/lib/build.ts +104 -2
  35. package/src/lib/dist.ts +2 -0
  36. package/src/lib/init.ts +7 -4
  37. package/src/lib/runtime/contracts.ts +52 -0
  38. package/src/lib/runtime/dispatcher.ts +185 -0
  39. package/src/lib/runtime/errors.ts +27 -0
  40. package/src/lib/runtime/factory.ts +182 -0
  41. package/src/lib/runtime/host.ts +72 -0
  42. package/src/lib/runtime/index.ts +36 -0
  43. package/src/lib/runtime/registration.ts +93 -0
  44. package/src/lib/runtime/transport.ts +93 -0
  45. package/src/lib/types/host.ts +71 -0
  46. package/src/lib/types/index.ts +7 -0
  47. package/src/lib/types/plugin.ts +110 -0
  48. package/src/lib/types/protocol.ts +97 -0
  49. package/src/lib/types/vcs.ts +579 -0
  50. package/test/build.test.js +147 -6
  51. package/test/cli.test.js +5 -3
  52. package/test/dist.test.js +27 -18
  53. package/test/init.test.js +29 -0
  54. package/test/runtime.test.js +235 -0
@@ -0,0 +1,459 @@
1
+ import type { RequestParams, RpcMethodHandler } from './protocol';
2
+ /** Describes the feature flags reported by a VCS backend plugin. */
3
+ export interface VcsCapabilities {
4
+ /** Indicates whether commit history operations are supported. */
5
+ commits: boolean;
6
+ /** Indicates whether branch operations are supported. */
7
+ branches: boolean;
8
+ /** Indicates whether tag operations are supported. */
9
+ tags: boolean;
10
+ /** Indicates whether index staging operations are supported. */
11
+ staging: boolean;
12
+ /** Indicates whether push and pull operations are supported. */
13
+ push_pull: boolean;
14
+ /** Indicates whether fast-forward helpers are supported. */
15
+ fast_forward: boolean;
16
+ }
17
+ /** Describes params that carry a repository session id. */
18
+ export interface VcsSessionParams extends RequestParams {
19
+ /** Stores the repository session id allocated by `vcs.open`. */
20
+ session_id: string;
21
+ }
22
+ /** Describes the params used by `vcs.open`. */
23
+ export interface VcsOpenParams extends RequestParams {
24
+ /** Stores the repository path to open. */
25
+ path: string;
26
+ }
27
+ /** Describes the result returned by `vcs.open`. */
28
+ export interface VcsSessionResult {
29
+ /** Stores the allocated repository session id. */
30
+ session_id: string;
31
+ }
32
+ /** Describes the params used by `vcs.clone_repo`. */
33
+ export interface VcsCloneRepoParams extends RequestParams {
34
+ /** Stores the source repository URL. */
35
+ url: string;
36
+ /** Stores the destination path. */
37
+ dest: string;
38
+ }
39
+ /** Describes one local branch kind marker. */
40
+ export interface VcsLocalBranchKind {
41
+ /** Stores the branch kind discriminator. */
42
+ type: 'Local';
43
+ }
44
+ /** Describes one remote branch kind marker. */
45
+ export interface VcsRemoteBranchKind {
46
+ /** Stores the branch kind discriminator. */
47
+ type: 'Remote';
48
+ /** Stores the remote name when known. */
49
+ remote: string | null;
50
+ }
51
+ /** Describes the branch kind union returned to the host. */
52
+ export type VcsBranchKind = VcsLocalBranchKind | VcsRemoteBranchKind;
53
+ /** Describes one branch entry returned by `vcs.list_branches`. */
54
+ export interface VcsBranchEntry {
55
+ /** Stores the short branch name. */
56
+ name: string;
57
+ /** Stores the full ref path. */
58
+ full_ref: string;
59
+ /** Stores the local or remote branch kind. */
60
+ kind: VcsBranchKind;
61
+ /** Indicates whether the branch is currently checked out. */
62
+ current: boolean;
63
+ }
64
+ /** Describes params for branch creation. */
65
+ export interface VcsCreateBranchParams extends VcsSessionParams {
66
+ /** Stores the new branch name. */
67
+ name: string;
68
+ /** Indicates whether the new branch should be checked out immediately. */
69
+ checkout?: boolean;
70
+ }
71
+ /** Describes params for branch checkout. */
72
+ export interface VcsCheckoutBranchParams extends VcsSessionParams {
73
+ /** Stores the branch name to check out. */
74
+ name: string;
75
+ }
76
+ /** Describes params for ensuring a remote exists. */
77
+ export interface VcsEnsureRemoteParams extends VcsSessionParams {
78
+ /** Stores the remote name. */
79
+ name: string;
80
+ /** Stores the remote URL. */
81
+ url: string;
82
+ }
83
+ /** Describes one remote entry. */
84
+ export interface VcsRemoteEntry {
85
+ /** Stores the remote name. */
86
+ name: string;
87
+ /** Stores the remote URL. */
88
+ url: string;
89
+ }
90
+ /** Describes params for removing a remote. */
91
+ export interface VcsRemoveRemoteParams extends VcsSessionParams {
92
+ /** Stores the remote name to remove. */
93
+ name: string;
94
+ }
95
+ /** Describes optional fetch flags. */
96
+ export interface VcsFetchOptions {
97
+ /** Indicates whether stale remote references should be pruned. */
98
+ prune?: boolean;
99
+ }
100
+ /** Describes params for fetch methods. */
101
+ export interface VcsFetchParams extends VcsSessionParams {
102
+ /** Stores the remote to fetch when one is supplied. */
103
+ remote?: string;
104
+ /** Stores the refspec to fetch when one is supplied. */
105
+ refspec?: string;
106
+ /** Stores optional fetch flags. */
107
+ opts?: VcsFetchOptions;
108
+ }
109
+ /** Describes params for push. */
110
+ export interface VcsPushParams extends VcsSessionParams {
111
+ /** Stores the remote to push to when one is supplied. */
112
+ remote?: string;
113
+ /** Stores the refspec to push when one is supplied. */
114
+ refspec?: string;
115
+ }
116
+ /** Describes params for fast-forward-only pull. */
117
+ export interface VcsPullFfOnlyParams extends VcsSessionParams {
118
+ /** Stores the remote to pull from when one is supplied. */
119
+ remote?: string;
120
+ /** Stores the branch to pull when one is supplied. */
121
+ branch?: string;
122
+ }
123
+ /** Describes params for commit creation. */
124
+ export interface VcsCommitParams extends VcsSessionParams {
125
+ /** Stores the commit author name. */
126
+ name: string;
127
+ /** Stores the commit author email. */
128
+ email: string;
129
+ /** Stores the commit message. */
130
+ message: string;
131
+ /** Stores the optional path subset to add before committing. */
132
+ paths?: string[];
133
+ }
134
+ /** Describes the aggregate repository status counts returned to the host. */
135
+ export interface StatusSummary {
136
+ /** Counts untracked files. */
137
+ untracked: number;
138
+ /** Counts modified working tree files. */
139
+ modified: number;
140
+ /** Counts staged files. */
141
+ staged: number;
142
+ /** Counts conflicted files. */
143
+ conflicted: number;
144
+ }
145
+ /** Describes one file entry in the OpenVCS status payload. */
146
+ export interface StatusFileEntry {
147
+ /** Stores the current file path. */
148
+ path: string;
149
+ /** Stores the prior path for rename and copy records. */
150
+ old_path: string | null;
151
+ /** Stores the porcelain status code. */
152
+ status: string;
153
+ /** Indicates whether the file has staged changes. */
154
+ staged: boolean;
155
+ /** Indicates whether a conflict has been resolved. */
156
+ resolved_conflict: boolean;
157
+ /** Stores placeholder hunk information until richer diff support exists. */
158
+ hunks: never[];
159
+ }
160
+ /** Describes the structured status payload returned to the host. */
161
+ export interface StatusPayload {
162
+ /** Stores file-level status entries. */
163
+ files: StatusFileEntry[];
164
+ /** Stores the local branch ahead count relative to its upstream. */
165
+ ahead: number;
166
+ /** Stores the local branch behind count relative to its upstream. */
167
+ behind: number;
168
+ }
169
+ /** Describes the complete parsed status result. */
170
+ export interface StatusParseResult {
171
+ /** Stores the aggregate status summary. */
172
+ summary: StatusSummary;
173
+ /** Stores the detailed status payload. */
174
+ payload: StatusPayload;
175
+ }
176
+ /** Describes one list-commits query payload. */
177
+ export interface VcsListCommitsQuery extends RequestParams {
178
+ /** Stores the number of commits to skip. */
179
+ skip?: number;
180
+ /** Stores the maximum number of commits to return. */
181
+ limit?: number;
182
+ /** Indicates whether topo-order should be used. */
183
+ topo_order?: boolean;
184
+ /** Indicates whether merge commits should be included. */
185
+ include_merges?: boolean;
186
+ /** Stores an optional author substring filter. */
187
+ author_contains?: string;
188
+ /** Stores an optional lower timestamp bound in UTC. */
189
+ since_utc?: string;
190
+ /** Stores an optional upper timestamp bound in UTC. */
191
+ until_utc?: string;
192
+ /** Stores the revision to walk from. */
193
+ rev?: string;
194
+ /** Stores an optional path filter. */
195
+ path?: string;
196
+ }
197
+ /** Describes params for `vcs.list_commits`. */
198
+ export interface VcsListCommitsParams extends VcsSessionParams {
199
+ /** Stores the query object sent by the host. */
200
+ query?: VcsListCommitsQuery;
201
+ }
202
+ /** Describes one commit entry returned from `git log`. */
203
+ export interface CommitEntry {
204
+ /** Stores the full commit id. */
205
+ id: string;
206
+ /** Stores the commit subject. */
207
+ msg: string;
208
+ /** Stores the author display name. */
209
+ author: string;
210
+ /** Stores the formatted metadata string. */
211
+ meta: string;
212
+ }
213
+ /** Describes params for `vcs.diff_file`. */
214
+ export interface VcsDiffFileParams extends VcsSessionParams {
215
+ /** Stores the file path to diff. */
216
+ path: string;
217
+ }
218
+ /** Describes params for `vcs.diff_commit`. */
219
+ export interface VcsDiffCommitParams extends VcsSessionParams {
220
+ /** Stores the revision to diff. */
221
+ rev: string;
222
+ }
223
+ /** Describes params for `vcs.get_conflict_details`. */
224
+ export interface VcsGetConflictDetailsParams extends VcsSessionParams {
225
+ /** Stores the conflicted path. */
226
+ path: string;
227
+ }
228
+ /** Describes one conflict details payload. */
229
+ export interface VcsConflictDetails {
230
+ /** Stores the conflicted path. */
231
+ path: string;
232
+ /** Stores the base version content when available. */
233
+ base: string | null;
234
+ /** Stores the current branch content when available. */
235
+ ours: string | null;
236
+ /** Stores the incoming branch content when available. */
237
+ theirs: string | null;
238
+ /** Indicates whether the conflict is binary. */
239
+ binary: boolean;
240
+ /** Indicates whether the conflict references Git LFS content. */
241
+ lfs_pointer: boolean;
242
+ }
243
+ /** Describes params for checking out one side of a conflict. */
244
+ export interface VcsCheckoutConflictSideParams extends VcsSessionParams {
245
+ /** Stores the side name, usually `ours` or `theirs`. */
246
+ side?: string;
247
+ /** Stores the conflicted file path. */
248
+ path: string;
249
+ }
250
+ /** Describes params for writing a merge result to disk. */
251
+ export interface VcsWriteMergeResultParams extends VcsSessionParams {
252
+ /** Stores the output file path. */
253
+ path: string;
254
+ /** Stores the file contents encoded as base64. */
255
+ content_b64: string;
256
+ }
257
+ /** Describes params for staging a text patch. */
258
+ export interface VcsStagePatchParams extends VcsSessionParams {
259
+ /** Stores the textual patch content. */
260
+ patch: string;
261
+ }
262
+ /** Describes params for discarding path changes. */
263
+ export interface VcsDiscardPathsParams extends VcsSessionParams {
264
+ /** Stores the paths to discard. */
265
+ paths?: string[];
266
+ }
267
+ /** Describes params for applying a reverse patch. */
268
+ export interface VcsApplyReversePatchParams extends VcsSessionParams {
269
+ /** Stores the textual patch content. */
270
+ patch: string;
271
+ }
272
+ /** Describes params for deleting a branch. */
273
+ export interface VcsDeleteBranchParams extends VcsSessionParams {
274
+ /** Stores the branch name to delete. */
275
+ name: string;
276
+ /** Indicates whether the delete should be forced. */
277
+ force?: boolean;
278
+ }
279
+ /** Describes params for renaming a branch. */
280
+ export interface VcsRenameBranchParams extends VcsSessionParams {
281
+ /** Stores the current branch name. */
282
+ old: string;
283
+ /** Stores the next branch name. */
284
+ new: string;
285
+ }
286
+ /** Describes params for merging another branch into the current branch. */
287
+ export interface VcsMergeIntoCurrentParams extends VcsSessionParams {
288
+ /** Stores the branch name to merge. */
289
+ name: string;
290
+ /** Stores an optional merge commit message. */
291
+ message?: string;
292
+ }
293
+ /** Describes params for setting a branch upstream. */
294
+ export interface VcsSetBranchUpstreamParams extends VcsSessionParams {
295
+ /** Stores the local branch name. */
296
+ branch: string;
297
+ /** Stores the upstream ref to track. */
298
+ upstream: string;
299
+ }
300
+ /** Describes params for getting a branch upstream. */
301
+ export interface VcsGetBranchUpstreamParams extends VcsSessionParams {
302
+ /** Stores the local branch name. */
303
+ branch: string;
304
+ }
305
+ /** Describes params for soft reset. */
306
+ export interface VcsResetSoftToParams extends VcsSessionParams {
307
+ /** Stores the revision to reset to. */
308
+ rev: string;
309
+ }
310
+ /** Describes one configured author identity. */
311
+ export interface VcsIdentity {
312
+ /** Stores the configured author name. */
313
+ name: string;
314
+ /** Stores the configured author email. */
315
+ email: string;
316
+ }
317
+ /** Describes params for configuring the local repository identity. */
318
+ export interface VcsSetIdentityLocalParams extends VcsSessionParams {
319
+ /** Stores the author name. */
320
+ name: string;
321
+ /** Stores the author email. */
322
+ email: string;
323
+ }
324
+ /** Describes one stash entry returned to the host. */
325
+ export interface StashEntry {
326
+ /** Stores the stash selector such as `stash@{0}`. */
327
+ selector: string;
328
+ /** Stores the stash message. */
329
+ msg: string;
330
+ /** Stores extra metadata reserved for future expansion. */
331
+ meta: string;
332
+ }
333
+ /** Describes params for stash push. */
334
+ export interface VcsStashPushParams extends VcsSessionParams {
335
+ /** Indicates whether untracked files should be included. */
336
+ include_untracked?: boolean;
337
+ /** Stores an optional stash message. */
338
+ message?: string;
339
+ }
340
+ /** Describes params for stash selection operations. */
341
+ export interface VcsStashSelectorParams extends VcsSessionParams {
342
+ /** Stores the stash selector, such as `stash@{0}`. */
343
+ selector: string;
344
+ }
345
+ /** Describes params for cherry-picking a commit. */
346
+ export interface VcsCherryPickParams extends VcsSessionParams {
347
+ /** Stores the commit to cherry-pick. */
348
+ commit: string;
349
+ }
350
+ /** Describes params for reverting a commit. */
351
+ export interface VcsRevertCommitParams extends VcsSessionParams {
352
+ /** Stores the commit to revert. */
353
+ commit: string;
354
+ /** Indicates whether the editor should be skipped. */
355
+ no_edit?: boolean;
356
+ }
357
+ /** Describes the delegate map supported by the SDK runtime for `vcs.*`. */
358
+ export interface VcsDelegates<TContext = unknown> {
359
+ /** Handles `vcs.get_caps`. */
360
+ 'vcs.get_caps'?: RpcMethodHandler<RequestParams, VcsCapabilities, TContext>;
361
+ /** Handles `vcs.open`. */
362
+ 'vcs.open'?: RpcMethodHandler<VcsOpenParams, VcsSessionResult, TContext>;
363
+ /** Handles `vcs.close`. */
364
+ 'vcs.close'?: RpcMethodHandler<VcsSessionParams, null, TContext>;
365
+ /** Handles `vcs.clone_repo`. */
366
+ 'vcs.clone_repo'?: RpcMethodHandler<VcsCloneRepoParams, null, TContext>;
367
+ /** Handles `vcs.get_workdir`. */
368
+ 'vcs.get_workdir'?: RpcMethodHandler<VcsSessionParams, string, TContext>;
369
+ /** Handles `vcs.get_current_branch`. */
370
+ 'vcs.get_current_branch'?: RpcMethodHandler<VcsSessionParams, string | null, TContext>;
371
+ /** Handles `vcs.list_branches`. */
372
+ 'vcs.list_branches'?: RpcMethodHandler<VcsSessionParams, VcsBranchEntry[], TContext>;
373
+ /** Handles `vcs.list_local_branches`. */
374
+ 'vcs.list_local_branches'?: RpcMethodHandler<VcsSessionParams, string[], TContext>;
375
+ /** Handles `vcs.create_branch`. */
376
+ 'vcs.create_branch'?: RpcMethodHandler<VcsCreateBranchParams, null, TContext>;
377
+ /** Handles `vcs.checkout_branch`. */
378
+ 'vcs.checkout_branch'?: RpcMethodHandler<VcsCheckoutBranchParams, null, TContext>;
379
+ /** Handles `vcs.ensure_remote`. */
380
+ 'vcs.ensure_remote'?: RpcMethodHandler<VcsEnsureRemoteParams, null, TContext>;
381
+ /** Handles `vcs.list_remotes`. */
382
+ 'vcs.list_remotes'?: RpcMethodHandler<VcsSessionParams, VcsRemoteEntry[], TContext>;
383
+ /** Handles `vcs.remove_remote`. */
384
+ 'vcs.remove_remote'?: RpcMethodHandler<VcsRemoveRemoteParams, null, TContext>;
385
+ /** Handles `vcs.fetch`. */
386
+ 'vcs.fetch'?: RpcMethodHandler<VcsFetchParams, null, TContext>;
387
+ /** Handles `vcs.fetch_with_options`. */
388
+ 'vcs.fetch_with_options'?: RpcMethodHandler<VcsFetchParams, null, TContext>;
389
+ /** Handles `vcs.push`. */
390
+ 'vcs.push'?: RpcMethodHandler<VcsPushParams, null, TContext>;
391
+ /** Handles `vcs.pull_ff_only`. */
392
+ 'vcs.pull_ff_only'?: RpcMethodHandler<VcsPullFfOnlyParams, null, TContext>;
393
+ /** Handles `vcs.commit`. */
394
+ 'vcs.commit'?: RpcMethodHandler<VcsCommitParams, string, TContext>;
395
+ /** Handles `vcs.commit_index`. */
396
+ 'vcs.commit_index'?: RpcMethodHandler<VcsCommitParams, string, TContext>;
397
+ /** Handles `vcs.get_status_summary`. */
398
+ 'vcs.get_status_summary'?: RpcMethodHandler<VcsSessionParams, StatusSummary, TContext>;
399
+ /** Handles `vcs.get_status_payload`. */
400
+ 'vcs.get_status_payload'?: RpcMethodHandler<VcsSessionParams, StatusPayload, TContext>;
401
+ /** Handles `vcs.list_commits`. */
402
+ 'vcs.list_commits'?: RpcMethodHandler<VcsListCommitsParams, CommitEntry[], TContext>;
403
+ /** Handles `vcs.diff_file`. */
404
+ 'vcs.diff_file'?: RpcMethodHandler<VcsDiffFileParams, string[], TContext>;
405
+ /** Handles `vcs.diff_commit`. */
406
+ 'vcs.diff_commit'?: RpcMethodHandler<VcsDiffCommitParams, string[], TContext>;
407
+ /** Handles `vcs.get_conflict_details`. */
408
+ 'vcs.get_conflict_details'?: RpcMethodHandler<VcsGetConflictDetailsParams, VcsConflictDetails, TContext>;
409
+ /** Handles `vcs.checkout_conflict_side`. */
410
+ 'vcs.checkout_conflict_side'?: RpcMethodHandler<VcsCheckoutConflictSideParams, null, TContext>;
411
+ /** Handles `vcs.write_merge_result`. */
412
+ 'vcs.write_merge_result'?: RpcMethodHandler<VcsWriteMergeResultParams, null, TContext>;
413
+ /** Handles `vcs.stage_patch`. */
414
+ 'vcs.stage_patch'?: RpcMethodHandler<VcsStagePatchParams, null, TContext>;
415
+ /** Handles `vcs.discard_paths`. */
416
+ 'vcs.discard_paths'?: RpcMethodHandler<VcsDiscardPathsParams, null, TContext>;
417
+ /** Handles `vcs.apply_reverse_patch`. */
418
+ 'vcs.apply_reverse_patch'?: RpcMethodHandler<VcsApplyReversePatchParams, null, TContext>;
419
+ /** Handles `vcs.delete_branch`. */
420
+ 'vcs.delete_branch'?: RpcMethodHandler<VcsDeleteBranchParams, null, TContext>;
421
+ /** Handles `vcs.rename_branch`. */
422
+ 'vcs.rename_branch'?: RpcMethodHandler<VcsRenameBranchParams, null, TContext>;
423
+ /** Handles `vcs.merge_into_current`. */
424
+ 'vcs.merge_into_current'?: RpcMethodHandler<VcsMergeIntoCurrentParams, null, TContext>;
425
+ /** Handles `vcs.merge_abort`. */
426
+ 'vcs.merge_abort'?: RpcMethodHandler<VcsSessionParams, null, TContext>;
427
+ /** Handles `vcs.merge_continue`. */
428
+ 'vcs.merge_continue'?: RpcMethodHandler<VcsSessionParams, null, TContext>;
429
+ /** Handles `vcs.is_merge_in_progress`. */
430
+ 'vcs.is_merge_in_progress'?: RpcMethodHandler<VcsSessionParams, boolean, TContext>;
431
+ /** Handles `vcs.set_branch_upstream`. */
432
+ 'vcs.set_branch_upstream'?: RpcMethodHandler<VcsSetBranchUpstreamParams, null, TContext>;
433
+ /** Handles `vcs.get_branch_upstream`. */
434
+ 'vcs.get_branch_upstream'?: RpcMethodHandler<VcsGetBranchUpstreamParams, string | null, TContext>;
435
+ /** Handles `vcs.hard_reset_head`. */
436
+ 'vcs.hard_reset_head'?: RpcMethodHandler<VcsSessionParams, null, TContext>;
437
+ /** Handles `vcs.reset_soft_to`. */
438
+ 'vcs.reset_soft_to'?: RpcMethodHandler<VcsResetSoftToParams, null, TContext>;
439
+ /** Handles `vcs.get_identity`. */
440
+ 'vcs.get_identity'?: RpcMethodHandler<VcsSessionParams, VcsIdentity | null, TContext>;
441
+ /** Handles `vcs.set_identity_local`. */
442
+ 'vcs.set_identity_local'?: RpcMethodHandler<VcsSetIdentityLocalParams, null, TContext>;
443
+ /** Handles `vcs.list_stashes`. */
444
+ 'vcs.list_stashes'?: RpcMethodHandler<VcsSessionParams, StashEntry[], TContext>;
445
+ /** Handles `vcs.stash_push`. */
446
+ 'vcs.stash_push'?: RpcMethodHandler<VcsStashPushParams, string, TContext>;
447
+ /** Handles `vcs.stash_apply`. */
448
+ 'vcs.stash_apply'?: RpcMethodHandler<VcsStashSelectorParams, null, TContext>;
449
+ /** Handles `vcs.stash_pop`. */
450
+ 'vcs.stash_pop'?: RpcMethodHandler<VcsStashSelectorParams, null, TContext>;
451
+ /** Handles `vcs.stash_drop`. */
452
+ 'vcs.stash_drop'?: RpcMethodHandler<VcsStashSelectorParams, null, TContext>;
453
+ /** Handles `vcs.stash_show`. */
454
+ 'vcs.stash_show'?: RpcMethodHandler<VcsStashSelectorParams, string, TContext>;
455
+ /** Handles `vcs.cherry_pick`. */
456
+ 'vcs.cherry_pick'?: RpcMethodHandler<VcsCherryPickParams, null, TContext>;
457
+ /** Handles `vcs.revert_commit`. */
458
+ 'vcs.revert_commit'?: RpcMethodHandler<VcsRevertCommitParams, null, TContext>;
459
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Copyright © 2025-2026 OpenVCS Contributors
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openvcs/sdk",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "OpenVCS SDK CLI for plugin scaffolding and .ovcsp tar.gz packaging",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "homepage": "https://openvcs.app/",
@@ -17,6 +17,19 @@
17
17
  "bin": {
18
18
  "openvcs": "bin/openvcs.js"
19
19
  },
20
+ "exports": {
21
+ "./runtime": {
22
+ "types": "./lib/runtime/index.d.ts",
23
+ "require": "./lib/runtime/index.js",
24
+ "default": "./lib/runtime/index.js"
25
+ },
26
+ "./types": {
27
+ "types": "./lib/types/index.d.ts",
28
+ "require": "./lib/types/index.js",
29
+ "default": "./lib/types/index.js"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
20
33
  "scripts": {
21
34
  "build": "node scripts/build-sdk.js",
22
35
  "test": "npm run build && node --test test/*.test.js",
package/src/lib/build.ts CHANGED
@@ -32,6 +32,8 @@ interface PackageScripts {
32
32
  [scriptName: string]: unknown;
33
33
  }
34
34
 
35
+ const AUTHORED_PLUGIN_MODULE_BASENAME = "plugin.js";
36
+
35
37
  /** Returns the npm executable name for the current platform. */
36
38
  export function npmExecutable(): string {
37
39
  return process.platform === "win32" ? "npm.cmd" : "npm";
@@ -137,6 +139,14 @@ export function validateDeclaredModuleExec(pluginDir: string, moduleExec: string
137
139
  return;
138
140
  }
139
141
 
142
+ const targetPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
143
+ if (!fs.existsSync(targetPath) || !fs.lstatSync(targetPath).isFile()) {
144
+ throw new Error(`module entrypoint not found at ${targetPath}`);
145
+ }
146
+ }
147
+
148
+ /** Resolves `module.exec` to an absolute path under `bin/`, rejecting invalid targets. */
149
+ function resolveDeclaredModuleExecPath(pluginDir: string, moduleExec: string): string {
140
150
  const normalizedExec = moduleExec.trim();
141
151
  const lowered = normalizedExec.toLowerCase();
142
152
  if (!lowered.endsWith(".js") && !lowered.endsWith(".mjs") && !lowered.endsWith(".cjs")) {
@@ -151,9 +161,100 @@ export function validateDeclaredModuleExec(pluginDir: string, moduleExec: string
151
161
  if (!isPathInside(binDir, targetPath) || targetPath === binDir) {
152
162
  throw new Error(`manifest module.exec must point to a file under bin/: ${moduleExec}`);
153
163
  }
154
- if (!fs.existsSync(targetPath) || !fs.lstatSync(targetPath).isFile()) {
155
- throw new Error(`module entrypoint not found at ${targetPath}`);
164
+
165
+ return targetPath;
166
+ }
167
+
168
+ /** Returns the compiled plugin module path imported by the generated bootstrap. */
169
+ export function authoredPluginModulePath(pluginDir: string): string {
170
+ return path.resolve(pluginDir, "bin", AUTHORED_PLUGIN_MODULE_BASENAME);
171
+ }
172
+
173
+ /** Ensures the plugin's authored module and generated bootstrap paths are compatible. */
174
+ export function validateGeneratedBootstrapTargets(
175
+ pluginDir: string,
176
+ moduleExec: string | undefined,
177
+ ): void {
178
+ if (!moduleExec) {
179
+ return;
180
+ }
181
+
182
+ resolveDeclaredModuleExecPath(pluginDir, moduleExec);
183
+ const normalizedExec = moduleExec.trim().toLowerCase();
184
+ if (normalizedExec === AUTHORED_PLUGIN_MODULE_BASENAME.toLowerCase()) {
185
+ throw new Error(
186
+ `manifest module.exec must not be ${AUTHORED_PLUGIN_MODULE_BASENAME}; SDK reserves bin/${AUTHORED_PLUGIN_MODULE_BASENAME} for the compiled OnPluginStart module`
187
+ );
188
+ }
189
+
190
+ const authoredModulePath = authoredPluginModulePath(pluginDir);
191
+ if (!fs.existsSync(authoredModulePath) || !fs.lstatSync(authoredModulePath).isFile()) {
192
+ throw new Error(
193
+ `compiled plugin module not found at ${authoredModulePath}; build:plugin must emit bin/${AUTHORED_PLUGIN_MODULE_BASENAME}`
194
+ );
195
+ }
196
+ }
197
+
198
+ /** Returns a normalized import specifier from one bin file to another. */
199
+ function relativeBinImport(fromExecPath: string, toModulePath: string): string {
200
+ const relativePath = path.relative(path.dirname(fromExecPath), toModulePath).replace(/\\/g, "/");
201
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
202
+ }
203
+
204
+ /** Validates that a module import path contains only safe characters for code generation. */
205
+ function validateModuleImportPath(importPath: string): void {
206
+ if (!/^[./a-zA-Z0-9_-]+$/.test(importPath)) {
207
+ throw new Error(
208
+ `unsafe module import path: ${importPath}; path must contain only alphanumeric characters, dots, slashes, hyphens, and underscores`
209
+ );
210
+ }
211
+ }
212
+
213
+ /** Renders the generated Node bootstrap that owns runtime startup. */
214
+ export function renderGeneratedBootstrap(
215
+ pluginModuleImportPath: string,
216
+ isEsm: boolean,
217
+ ): string {
218
+ validateModuleImportPath(pluginModuleImportPath);
219
+
220
+ if (isEsm) {
221
+ return `#!/usr/bin/env node\n// Copyright © 2025-2026 OpenVCS Contributors\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nimport { bootstrapPluginModule } from '@openvcs/sdk/runtime';\n\nawait bootstrapPluginModule({\n importPluginModule: async () => import('${pluginModuleImportPath}'),\n modulePath: '${pluginModuleImportPath}',\n});\n`;
222
+ }
223
+
224
+ 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`;
225
+ }
226
+
227
+ /** Writes the generated SDK-owned module entrypoint under `bin/<module.exec>`. */
228
+ export function generateModuleBootstrap(pluginDir: string, moduleExec: string | undefined): void {
229
+ if (!moduleExec) {
230
+ console.debug(`generateModuleBootstrap: no module.exec defined, skipping bootstrap generation for ${pluginDir}`);
231
+ return;
232
+ }
233
+
234
+ validateGeneratedBootstrapTargets(pluginDir, moduleExec);
235
+ const execPath = resolveDeclaredModuleExecPath(pluginDir, moduleExec);
236
+ const pluginModulePath = authoredPluginModulePath(pluginDir);
237
+ const pluginModuleImportPath = relativeBinImport(execPath, pluginModulePath);
238
+ const isEsm = detectEsmMode(pluginDir, moduleExec);
239
+
240
+ fs.mkdirSync(path.dirname(execPath), { recursive: true });
241
+ fs.writeFileSync(execPath, renderGeneratedBootstrap(pluginModuleImportPath, isEsm), "utf8");
242
+ }
243
+
244
+ /** Detects whether the plugin runs in ESM mode based on package.json or file extension. */
245
+ function detectEsmMode(pluginDir: string, moduleExec: string): boolean {
246
+ const packageJsonPath = path.join(pluginDir, "package.json");
247
+ if (fs.existsSync(packageJsonPath) && fs.lstatSync(packageJsonPath).isFile()) {
248
+ try {
249
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
250
+ if (packageJson.type === "module") {
251
+ return true;
252
+ }
253
+ } catch {
254
+ // Ignore JSON parse errors
255
+ }
156
256
  }
257
+ return moduleExec.trim().endsWith(".mjs");
157
258
  }
158
259
 
159
260
  /** Returns whether the plugin repository has a `package.json`. */
@@ -224,6 +325,7 @@ export function buildPluginAssets(parsedArgs: BuildArgs): ManifestInfo {
224
325
  }
225
326
 
226
327
  runCommand(npmExecutable(), ["run", "build:plugin"], parsedArgs.pluginDir, parsedArgs.verbose);
328
+ generateModuleBootstrap(parsedArgs.pluginDir, manifest.moduleExec);
227
329
  validateDeclaredModuleExec(parsedArgs.pluginDir, manifest.moduleExec);
228
330
  return manifest;
229
331
  }
package/src/lib/dist.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  readManifest,
10
10
  runCommand,
11
11
  validateDeclaredModuleExec,
12
+ validateGeneratedBootstrapTargets,
12
13
  } from "./build";
13
14
  import {
14
15
  copyDirectoryRecursiveStrict,
@@ -251,6 +252,7 @@ export async function bundlePlugin(parsedArgs: DistArgs): Promise<string> {
251
252
  if (!moduleExec && !hasThemes && !entry) {
252
253
  throw new Error("manifest has no module.exec, entry, or themes/");
253
254
  }
255
+ validateGeneratedBootstrapTargets(pluginDir, moduleExec);
254
256
  validateDeclaredModuleExec(pluginDir, moduleExec);
255
257
 
256
258
  ensureDirectory(outDir);