@ebowwa/pkg-ops 0.1.16 → 0.1.18
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/dist/bridge.d.ts +63 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/config.d.ts +55 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -13
- package/dist/index.js.map +6 -6
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/bridge.ts +87 -0
- package/src/config.ts +173 -1
- package/src/index.ts +332 -7
- package/src/types.ts +240 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for PkgOps multi-version support.
|
|
3
|
+
*
|
|
4
|
+
* @module @ebowwa/pkg-ops/types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Metadata for an installed package version.
|
|
8
|
+
*/
|
|
9
|
+
export interface VersionInfo {
|
|
10
|
+
/** Semver version string */
|
|
11
|
+
version: string;
|
|
12
|
+
/** ISO timestamp when this version was installed */
|
|
13
|
+
installedAt: string;
|
|
14
|
+
/** Size of the dist directory in bytes */
|
|
15
|
+
distSizeBytes: number | null;
|
|
16
|
+
/** Number of files in the dist directory */
|
|
17
|
+
fileCount: number | null;
|
|
18
|
+
/** Whether this is the currently active version */
|
|
19
|
+
active: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Configuration for a managed package with multi-version support.
|
|
23
|
+
*/
|
|
24
|
+
export interface PackageConfig {
|
|
25
|
+
/** Currently active version */
|
|
26
|
+
version: string;
|
|
27
|
+
/** All installed versions with metadata */
|
|
28
|
+
versions: Record<string, VersionMetadata>;
|
|
29
|
+
/** Associated systemd service name (without .service suffix) */
|
|
30
|
+
service?: string;
|
|
31
|
+
/** Whether to auto-start the service after install */
|
|
32
|
+
autoStart?: boolean;
|
|
33
|
+
/** Custom environment variables for the service */
|
|
34
|
+
environment?: Record<string, string>;
|
|
35
|
+
/** Maximum number of versions to keep (default: 3) */
|
|
36
|
+
maxVersions?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Metadata stored for each installed version.
|
|
40
|
+
*/
|
|
41
|
+
export interface VersionMetadata {
|
|
42
|
+
/** ISO timestamp when this version was installed */
|
|
43
|
+
installedAt: string;
|
|
44
|
+
/** Size of the dist directory in bytes */
|
|
45
|
+
distSizeBytes: number | null;
|
|
46
|
+
/** Number of files in the dist directory */
|
|
47
|
+
fileCount: number | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Result of listing versions for a package.
|
|
51
|
+
*/
|
|
52
|
+
export interface VersionsListResult {
|
|
53
|
+
packageName: string;
|
|
54
|
+
versions: VersionInfo[];
|
|
55
|
+
activeVersion: string;
|
|
56
|
+
maxVersions: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Result of switching to a different version.
|
|
60
|
+
*/
|
|
61
|
+
export interface SwitchVersionResult {
|
|
62
|
+
success: boolean;
|
|
63
|
+
packageName: string;
|
|
64
|
+
previousVersion: string;
|
|
65
|
+
newVersion: string;
|
|
66
|
+
serviceRestarted: boolean;
|
|
67
|
+
message: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Result of pruning old versions.
|
|
71
|
+
*/
|
|
72
|
+
export interface PruneResult {
|
|
73
|
+
success: boolean;
|
|
74
|
+
packageName: string;
|
|
75
|
+
removedVersions: string[];
|
|
76
|
+
keptVersions: string[];
|
|
77
|
+
freedBytes: number;
|
|
78
|
+
message: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Result of installing a package (updated for multi-version).
|
|
82
|
+
*/
|
|
83
|
+
export interface InstallResult {
|
|
84
|
+
success: boolean;
|
|
85
|
+
version: string;
|
|
86
|
+
previousVersion?: string;
|
|
87
|
+
/** Whether this was a new version install or reinstall */
|
|
88
|
+
isNew: boolean;
|
|
89
|
+
/** Total versions now installed */
|
|
90
|
+
totalVersions: number;
|
|
91
|
+
message: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Result of rolling back a package.
|
|
95
|
+
*/
|
|
96
|
+
export interface RollbackResult {
|
|
97
|
+
success: boolean;
|
|
98
|
+
previousVersion: string;
|
|
99
|
+
currentVersion: string;
|
|
100
|
+
/** Available versions to rollback to */
|
|
101
|
+
availableVersions: string[];
|
|
102
|
+
message: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Package info with version details.
|
|
106
|
+
*/
|
|
107
|
+
export interface PackageInfo {
|
|
108
|
+
name: string;
|
|
109
|
+
version: string;
|
|
110
|
+
installed: boolean;
|
|
111
|
+
service?: string;
|
|
112
|
+
/** Total installed versions */
|
|
113
|
+
totalVersions?: number;
|
|
114
|
+
/** All installed versions */
|
|
115
|
+
versions?: VersionInfo[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Main configuration structure.
|
|
119
|
+
*/
|
|
120
|
+
export interface PkgOpsConfig {
|
|
121
|
+
/** Managed packages */
|
|
122
|
+
packages: Record<string, PackageConfig>;
|
|
123
|
+
/** Health check HTTP port (default: 8914) */
|
|
124
|
+
healthPort?: number;
|
|
125
|
+
/** Working directory for installations (default: /root) */
|
|
126
|
+
workDir?: string;
|
|
127
|
+
/** Log level (default: "info") */
|
|
128
|
+
logLevel?: "debug" | "info" | "warn" | "error";
|
|
129
|
+
/** Default max versions to keep per package */
|
|
130
|
+
defaultMaxVersions?: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* JSON-RPC request structure.
|
|
134
|
+
*/
|
|
135
|
+
export interface JsonRpcRequest {
|
|
136
|
+
jsonrpc: "2.0";
|
|
137
|
+
id: string;
|
|
138
|
+
method: string;
|
|
139
|
+
params?: Record<string, unknown>;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* JSON-RPC response structure.
|
|
143
|
+
*/
|
|
144
|
+
export interface JsonRpcResponse {
|
|
145
|
+
jsonrpc: "2.0";
|
|
146
|
+
id: string;
|
|
147
|
+
result?: unknown;
|
|
148
|
+
error?: {
|
|
149
|
+
code: number;
|
|
150
|
+
message: string;
|
|
151
|
+
data?: unknown;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Vulnerability audit result.
|
|
156
|
+
*/
|
|
157
|
+
export interface AuditResult {
|
|
158
|
+
packageName: string;
|
|
159
|
+
severity: string;
|
|
160
|
+
vulnerability: string;
|
|
161
|
+
description: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Package verification result.
|
|
165
|
+
*/
|
|
166
|
+
export interface VerifyResult {
|
|
167
|
+
packageName: string;
|
|
168
|
+
version: string;
|
|
169
|
+
success: boolean;
|
|
170
|
+
distExists: boolean;
|
|
171
|
+
checksum: string | null;
|
|
172
|
+
message: string;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Bundle size info.
|
|
176
|
+
*/
|
|
177
|
+
export interface BundleSize {
|
|
178
|
+
packageName: string;
|
|
179
|
+
version: string;
|
|
180
|
+
distSizeBytes: number;
|
|
181
|
+
fileCount: number;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Installed package detailed info.
|
|
185
|
+
*/
|
|
186
|
+
export interface InstalledPackageInfo {
|
|
187
|
+
packageName: string;
|
|
188
|
+
version: string;
|
|
189
|
+
distSizeBytes: number | null;
|
|
190
|
+
installedAt: string | null;
|
|
191
|
+
totalVersions?: number;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Health check result.
|
|
195
|
+
*/
|
|
196
|
+
export interface HealthCheckResult {
|
|
197
|
+
healthy: boolean;
|
|
198
|
+
services: Array<{
|
|
199
|
+
name: string;
|
|
200
|
+
status: string;
|
|
201
|
+
pid: number;
|
|
202
|
+
}>;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/bridge.ts
CHANGED
|
@@ -88,6 +88,44 @@ export interface InstalledPackageInfo {
|
|
|
88
88
|
version: string;
|
|
89
89
|
distSizeBytes: number | null;
|
|
90
90
|
installedAt: string | null;
|
|
91
|
+
/** Total number of installed versions */
|
|
92
|
+
totalVersions?: number;
|
|
93
|
+
/** All installed versions */
|
|
94
|
+
versions?: VersionInfo[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Version information for a package.
|
|
99
|
+
*/
|
|
100
|
+
export interface VersionInfo {
|
|
101
|
+
version: string;
|
|
102
|
+
installedAt: string;
|
|
103
|
+
distSizeBytes: number | null;
|
|
104
|
+
fileCount: number | null;
|
|
105
|
+
active: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Result of switching versions.
|
|
110
|
+
*/
|
|
111
|
+
export interface SwitchResult {
|
|
112
|
+
success: boolean;
|
|
113
|
+
packageName: string;
|
|
114
|
+
fromVersion: string;
|
|
115
|
+
toVersion: string;
|
|
116
|
+
message: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Result of pruning old versions.
|
|
121
|
+
*/
|
|
122
|
+
export interface PruneResult {
|
|
123
|
+
success: boolean;
|
|
124
|
+
packageName: string;
|
|
125
|
+
removedVersions: string[];
|
|
126
|
+
keptVersions: string[];
|
|
127
|
+
freedBytes: number;
|
|
128
|
+
message: string;
|
|
91
129
|
}
|
|
92
130
|
|
|
93
131
|
interface JsonRpcRequest {
|
|
@@ -281,6 +319,55 @@ export class RustBridge {
|
|
|
281
319
|
return this.sendRequest("installedInfo", {}) as Promise<InstalledPackageInfo[]>;
|
|
282
320
|
}
|
|
283
321
|
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Multi-Version Methods
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* List all installed versions of a package.
|
|
328
|
+
*/
|
|
329
|
+
async listVersions(packageName: string): Promise<VersionInfo[]> {
|
|
330
|
+
return this.sendRequest("listVersions", { packageName }) as Promise<VersionInfo[]>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Switch to a specific installed version.
|
|
335
|
+
*/
|
|
336
|
+
async switchVersion(packageName: string, version: string): Promise<SwitchResult> {
|
|
337
|
+
return this.sendRequest("switchVersion", { packageName, version }) as Promise<SwitchResult>;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Remove old versions, keeping only the N most recent.
|
|
342
|
+
*/
|
|
343
|
+
async pruneVersions(packageName: string, keepCount: number): Promise<PruneResult> {
|
|
344
|
+
return this.sendRequest("pruneVersions", { packageName, keepCount }) as Promise<PruneResult>;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Remove a specific version.
|
|
349
|
+
*/
|
|
350
|
+
async removeVersion(packageName: string, version: string): Promise<{ success: boolean; message: string }> {
|
|
351
|
+
return this.sendRequest("removeVersion", { packageName, version }) as Promise<{ success: boolean; message: string }>;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Get packages with multiple versions installed.
|
|
356
|
+
*/
|
|
357
|
+
async getMultiVersionPackages(): Promise<Array<{
|
|
358
|
+
packageName: string;
|
|
359
|
+
activeVersion: string;
|
|
360
|
+
totalVersions: number;
|
|
361
|
+
versions: string[];
|
|
362
|
+
}>> {
|
|
363
|
+
return this.sendRequest("getMultiVersionPackages", {}) as Promise<Array<{
|
|
364
|
+
packageName: string;
|
|
365
|
+
activeVersion: string;
|
|
366
|
+
totalVersions: number;
|
|
367
|
+
versions: string[];
|
|
368
|
+
}>>;
|
|
369
|
+
}
|
|
370
|
+
|
|
284
371
|
// ---------------------------------------------------------------------------
|
|
285
372
|
// Private Methods
|
|
286
373
|
// ---------------------------------------------------------------------------
|
package/src/config.ts
CHANGED
|
@@ -23,9 +23,23 @@ import { dirname } from "node:path";
|
|
|
23
23
|
// Types
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Metadata for a single installed version.
|
|
28
|
+
*/
|
|
29
|
+
export interface VersionMetadata {
|
|
30
|
+
/** ISO timestamp when this version was installed */
|
|
31
|
+
installedAt: string;
|
|
32
|
+
/** Size of the dist directory in bytes */
|
|
33
|
+
distSizeBytes: number | null;
|
|
34
|
+
/** Number of files in dist */
|
|
35
|
+
fileCount: number | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
export interface PackageConfig {
|
|
27
|
-
/**
|
|
39
|
+
/** Currently active version (semver) */
|
|
28
40
|
version: string;
|
|
41
|
+
/** All installed versions with metadata */
|
|
42
|
+
versions: Record<string, VersionMetadata>;
|
|
29
43
|
/** Associated systemd service name (without .service suffix) */
|
|
30
44
|
service?: string;
|
|
31
45
|
/** Whether to auto-start the service after install */
|
|
@@ -190,3 +204,161 @@ export function parsePackageSpec(spec: string): { name: string; version: string
|
|
|
190
204
|
|
|
191
205
|
return { name, version };
|
|
192
206
|
}
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Multi-Version Helper Functions
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get all installed versions for a package.
|
|
214
|
+
*/
|
|
215
|
+
export function getInstalledVersions(packageName: string): VersionMetadata[] {
|
|
216
|
+
const config = loadConfig();
|
|
217
|
+
const pkgConfig = config.packages[packageName];
|
|
218
|
+
if (!pkgConfig?.versions) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
return Object.entries(pkgConfig.versions).map(([version, meta]) => ({
|
|
222
|
+
version,
|
|
223
|
+
...meta,
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check if a specific version is installed.
|
|
229
|
+
*/
|
|
230
|
+
export function isVersionInstalled(packageName: string, version: string): boolean {
|
|
231
|
+
const config = loadConfig();
|
|
232
|
+
const pkgConfig = config.packages[packageName];
|
|
233
|
+
return !!(pkgConfig?.versions?.[version]);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get the active version for a package.
|
|
238
|
+
*/
|
|
239
|
+
export function getActiveVersion(packageName: string): string | null {
|
|
240
|
+
const config = loadConfig();
|
|
241
|
+
const pkgConfig = config.packages[packageName];
|
|
242
|
+
return pkgConfig?.version ?? null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Add a new version to the package config.
|
|
247
|
+
*/
|
|
248
|
+
export function addPackageVersion(
|
|
249
|
+
packageName: string,
|
|
250
|
+
version: string,
|
|
251
|
+
metadata: Omit<VersionMetadata, "installedAt"> & { installedAt?: string }
|
|
252
|
+
): void {
|
|
253
|
+
const config = loadConfig();
|
|
254
|
+
|
|
255
|
+
if (!config.packages[packageName]) {
|
|
256
|
+
config.packages[packageName] = {
|
|
257
|
+
version,
|
|
258
|
+
versions: {},
|
|
259
|
+
service: packageName.replace("@ebowwa/", ""),
|
|
260
|
+
autoStart: true,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
config.packages[packageName].versions[version] = {
|
|
265
|
+
installedAt: metadata.installedAt ?? new Date().toISOString(),
|
|
266
|
+
distSizeBytes: metadata.distSizeBytes ?? null,
|
|
267
|
+
fileCount: metadata.fileCount ?? null,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
saveConfig(config);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Remove a version from the package config.
|
|
275
|
+
* Returns true if the version was removed, false if it didn't exist.
|
|
276
|
+
*/
|
|
277
|
+
export function removePackageVersion(packageName: string, version: string): boolean {
|
|
278
|
+
const config = loadConfig();
|
|
279
|
+
const pkgConfig = config.packages[packageName];
|
|
280
|
+
|
|
281
|
+
if (!pkgConfig?.versions?.[version]) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
delete pkgConfig.versions[version];
|
|
286
|
+
|
|
287
|
+
// If removing the active version, switch to the most recent remaining version
|
|
288
|
+
if (pkgConfig.version === version) {
|
|
289
|
+
const remainingVersions = Object.keys(pkgConfig.versions).sort((a, b) => {
|
|
290
|
+
// Sort by installedAt descending (most recent first)
|
|
291
|
+
const aTime = pkgConfig.versions[a]?.installedAt ?? "";
|
|
292
|
+
const bTime = pkgConfig.versions[b]?.installedAt ?? "";
|
|
293
|
+
return bTime.localeCompare(aTime);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
if (remainingVersions.length > 0) {
|
|
297
|
+
pkgConfig.version = remainingVersions[0];
|
|
298
|
+
} else {
|
|
299
|
+
// No versions left, remove the package entirely
|
|
300
|
+
delete config.packages[packageName];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
saveConfig(config);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Set the active version for a package.
|
|
310
|
+
* Returns true if successful, false if version not installed.
|
|
311
|
+
*/
|
|
312
|
+
export function setActiveVersion(packageName: string, version: string): boolean {
|
|
313
|
+
const config = loadConfig();
|
|
314
|
+
const pkgConfig = config.packages[packageName];
|
|
315
|
+
|
|
316
|
+
if (!pkgConfig?.versions?.[version]) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
pkgConfig.version = version;
|
|
321
|
+
saveConfig(config);
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get the count of installed versions for a package.
|
|
327
|
+
*/
|
|
328
|
+
export function getVersionCount(packageName: string): number {
|
|
329
|
+
const config = loadConfig();
|
|
330
|
+
const pkgConfig = config.packages[packageName];
|
|
331
|
+
return Object.keys(pkgConfig?.versions ?? {}).length;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get packages with multiple versions installed.
|
|
336
|
+
*/
|
|
337
|
+
export function getPackagesWithMultipleVersions(): Array<{
|
|
338
|
+
name: string;
|
|
339
|
+
activeVersion: string;
|
|
340
|
+
totalVersions: number;
|
|
341
|
+
versions: string[];
|
|
342
|
+
}> {
|
|
343
|
+
const config = loadConfig();
|
|
344
|
+
const result: Array<{
|
|
345
|
+
name: string;
|
|
346
|
+
activeVersion: string;
|
|
347
|
+
totalVersions: number;
|
|
348
|
+
versions: string[];
|
|
349
|
+
}> = [];
|
|
350
|
+
|
|
351
|
+
for (const [name, pkgConfig] of Object.entries(config.packages)) {
|
|
352
|
+
const versions = Object.keys(pkgConfig.versions ?? {});
|
|
353
|
+
if (versions.length > 1) {
|
|
354
|
+
result.push({
|
|
355
|
+
name,
|
|
356
|
+
activeVersion: pkgConfig.version,
|
|
357
|
+
totalVersions: versions.length,
|
|
358
|
+
versions: versions.sort((a, b) => b.localeCompare(a, undefined, { numeric: true })),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return result;
|
|
364
|
+
}
|