@prompd/cli 0.3.3
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 +162 -0
- package/bin/prompd.js +23 -0
- package/dist/commands/cache.d.ts +3 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +199 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/compile.d.ts +9 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +104 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +212 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +183 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deps.d.ts +3 -0
- package/dist/commands/deps.d.ts.map +1 -0
- package/dist/commands/deps.js +192 -0
- package/dist/commands/deps.js.map +1 -0
- package/dist/commands/explain.d.ts +3 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +227 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/git.d.ts +3 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +306 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +177 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +126 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +326 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/namespace.d.ts +3 -0
- package/dist/commands/namespace.d.ts.map +1 -0
- package/dist/commands/namespace.js +113 -0
- package/dist/commands/namespace.js.map +1 -0
- package/dist/commands/package.d.ts +23 -0
- package/dist/commands/package.d.ts.map +1 -0
- package/dist/commands/package.js +746 -0
- package/dist/commands/package.js.map +1 -0
- package/dist/commands/provider.d.ts +3 -0
- package/dist/commands/provider.d.ts.map +1 -0
- package/dist/commands/provider.js +285 -0
- package/dist/commands/provider.js.map +1 -0
- package/dist/commands/registry.d.ts +9 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +361 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +157 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/show.d.ts +3 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +90 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +95 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +57 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +166 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +388 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.d.ts +164 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +388 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/compiler/file-system.d.ts +178 -0
- package/dist/lib/compiler/file-system.d.ts.map +1 -0
- package/dist/lib/compiler/file-system.js +440 -0
- package/dist/lib/compiler/file-system.js.map +1 -0
- package/dist/lib/compiler/formatters/anthropic.d.ts +21 -0
- package/dist/lib/compiler/formatters/anthropic.d.ts.map +1 -0
- package/dist/lib/compiler/formatters/anthropic.js +95 -0
- package/dist/lib/compiler/formatters/anthropic.js.map +1 -0
- package/dist/lib/compiler/formatters/markdown.d.ts +17 -0
- package/dist/lib/compiler/formatters/markdown.d.ts.map +1 -0
- package/dist/lib/compiler/formatters/markdown.js +114 -0
- package/dist/lib/compiler/formatters/markdown.js.map +1 -0
- package/dist/lib/compiler/formatters/openai.d.ts +21 -0
- package/dist/lib/compiler/formatters/openai.d.ts.map +1 -0
- package/dist/lib/compiler/formatters/openai.js +98 -0
- package/dist/lib/compiler/formatters/openai.js.map +1 -0
- package/dist/lib/compiler/index.d.ts +56 -0
- package/dist/lib/compiler/index.d.ts.map +1 -0
- package/dist/lib/compiler/index.js +165 -0
- package/dist/lib/compiler/index.js.map +1 -0
- package/dist/lib/compiler/language-map.d.ts +31 -0
- package/dist/lib/compiler/language-map.d.ts.map +1 -0
- package/dist/lib/compiler/language-map.js +156 -0
- package/dist/lib/compiler/language-map.js.map +1 -0
- package/dist/lib/compiler/package-resolver.d.ts +68 -0
- package/dist/lib/compiler/package-resolver.d.ts.map +1 -0
- package/dist/lib/compiler/package-resolver.js +254 -0
- package/dist/lib/compiler/package-resolver.js.map +1 -0
- package/dist/lib/compiler/pipeline.d.ts +53 -0
- package/dist/lib/compiler/pipeline.d.ts.map +1 -0
- package/dist/lib/compiler/pipeline.js +209 -0
- package/dist/lib/compiler/pipeline.js.map +1 -0
- package/dist/lib/compiler/prompd-loader.d.ts +108 -0
- package/dist/lib/compiler/prompd-loader.d.ts.map +1 -0
- package/dist/lib/compiler/prompd-loader.js +270 -0
- package/dist/lib/compiler/prompd-loader.js.map +1 -0
- package/dist/lib/compiler/section-override.d.ts +40 -0
- package/dist/lib/compiler/section-override.d.ts.map +1 -0
- package/dist/lib/compiler/section-override.js +296 -0
- package/dist/lib/compiler/section-override.js.map +1 -0
- package/dist/lib/compiler/stages/assets.d.ts +71 -0
- package/dist/lib/compiler/stages/assets.d.ts.map +1 -0
- package/dist/lib/compiler/stages/assets.js +456 -0
- package/dist/lib/compiler/stages/assets.js.map +1 -0
- package/dist/lib/compiler/stages/codegen.d.ts +17 -0
- package/dist/lib/compiler/stages/codegen.d.ts.map +1 -0
- package/dist/lib/compiler/stages/codegen.js +64 -0
- package/dist/lib/compiler/stages/codegen.js.map +1 -0
- package/dist/lib/compiler/stages/dependency.d.ts +38 -0
- package/dist/lib/compiler/stages/dependency.d.ts.map +1 -0
- package/dist/lib/compiler/stages/dependency.js +307 -0
- package/dist/lib/compiler/stages/dependency.js.map +1 -0
- package/dist/lib/compiler/stages/lexical.d.ts +19 -0
- package/dist/lib/compiler/stages/lexical.d.ts.map +1 -0
- package/dist/lib/compiler/stages/lexical.js +92 -0
- package/dist/lib/compiler/stages/lexical.js.map +1 -0
- package/dist/lib/compiler/stages/semantic.d.ts +20 -0
- package/dist/lib/compiler/stages/semantic.d.ts.map +1 -0
- package/dist/lib/compiler/stages/semantic.js +166 -0
- package/dist/lib/compiler/stages/semantic.js.map +1 -0
- package/dist/lib/compiler/stages/template.d.ts +94 -0
- package/dist/lib/compiler/stages/template.d.ts.map +1 -0
- package/dist/lib/compiler/stages/template.js +1044 -0
- package/dist/lib/compiler/stages/template.js.map +1 -0
- package/dist/lib/compiler/types.d.ts +200 -0
- package/dist/lib/compiler/types.d.ts.map +1 -0
- package/dist/lib/compiler/types.js +137 -0
- package/dist/lib/compiler/types.js.map +1 -0
- package/dist/lib/config.d.ts +29 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +375 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/errors.d.ts +19 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +47 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/executor.d.ts +18 -0
- package/dist/lib/executor.d.ts.map +1 -0
- package/dist/lib/executor.js +372 -0
- package/dist/lib/executor.js.map +1 -0
- package/dist/lib/git.d.ts +74 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +254 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/index.d.ts +43 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +108 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/mcp.d.ts +42 -0
- package/dist/lib/mcp.d.ts.map +1 -0
- package/dist/lib/mcp.js +477 -0
- package/dist/lib/mcp.js.map +1 -0
- package/dist/lib/model-updater.d.ts +51 -0
- package/dist/lib/model-updater.d.ts.map +1 -0
- package/dist/lib/model-updater.js +275 -0
- package/dist/lib/model-updater.js.map +1 -0
- package/dist/lib/parser.d.ts +9 -0
- package/dist/lib/parser.d.ts.map +1 -0
- package/dist/lib/parser.js +197 -0
- package/dist/lib/parser.js.map +1 -0
- package/dist/lib/registry.d.ts +183 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/registry.js +786 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/rpc-server.d.ts +78 -0
- package/dist/lib/rpc-server.d.ts.map +1 -0
- package/dist/lib/rpc-server.js +404 -0
- package/dist/lib/rpc-server.js.map +1 -0
- package/dist/lib/security.d.ts +120 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +478 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/validation.d.ts +106 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +398 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/version.d.ts +29 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +202 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/workflow-engine.d.ts +161 -0
- package/dist/lib/workflow-engine.d.ts.map +1 -0
- package/dist/lib/workflow-engine.js +422 -0
- package/dist/lib/workflow-engine.js.map +1 -0
- package/dist/lib/workflow.d.ts +102 -0
- package/dist/lib/workflow.d.ts.map +1 -0
- package/dist/lib/workflow.js +228 -0
- package/dist/lib/workflow.js.map +1 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +134 -0
- package/dist/server.js.map +1 -0
- package/dist/types/index.d.ts +116 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +144 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompd Package Registry System (Multi-Registry Architecture)
|
|
4
|
+
* Core registry infrastructure for publishing and installing packages with scope-based resolution
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.createDefaultRegistryConfig = exports.RegistryClient = void 0;
|
|
41
|
+
const fs = __importStar(require("fs-extra"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const semver = __importStar(require("semver"));
|
|
44
|
+
const tar = __importStar(require("tar"));
|
|
45
|
+
const events_1 = require("events");
|
|
46
|
+
const security_1 = require("./security");
|
|
47
|
+
const config_1 = require("./config");
|
|
48
|
+
const file_system_1 = require("./compiler/file-system");
|
|
49
|
+
/**
|
|
50
|
+
* Package Registry Client
|
|
51
|
+
*/
|
|
52
|
+
class RegistryClient extends events_1.EventEmitter {
|
|
53
|
+
constructor(options) {
|
|
54
|
+
super();
|
|
55
|
+
this.cache = new Map();
|
|
56
|
+
// Handle legacy string parameter (registry name)
|
|
57
|
+
const opts = typeof options === 'string'
|
|
58
|
+
? { registryName: options }
|
|
59
|
+
: options || {};
|
|
60
|
+
const configManager = config_1.ConfigManager.getInstance();
|
|
61
|
+
// Load config synchronously - this is critical!
|
|
62
|
+
this.config = this.loadConfigSync(configManager);
|
|
63
|
+
// Resolve registry name
|
|
64
|
+
this.registryName = opts.registryName || this.config.registry.default || 'prompdhub';
|
|
65
|
+
// Get registry config
|
|
66
|
+
const registries = this.config.registry.registries;
|
|
67
|
+
if (!registries[this.registryName]) {
|
|
68
|
+
throw new Error(`Registry '${this.registryName}' not found in configuration`);
|
|
69
|
+
}
|
|
70
|
+
this.registryConfig = registries[this.registryName];
|
|
71
|
+
// Override URL if provided directly (for server-side usage)
|
|
72
|
+
if (opts.registryUrl) {
|
|
73
|
+
this.registryConfig = { ...this.registryConfig, url: opts.registryUrl };
|
|
74
|
+
}
|
|
75
|
+
this.ensureCacheDir();
|
|
76
|
+
}
|
|
77
|
+
get registryUrl() {
|
|
78
|
+
return this.registryConfig.url;
|
|
79
|
+
}
|
|
80
|
+
get authToken() {
|
|
81
|
+
return this.registryConfig.token;
|
|
82
|
+
}
|
|
83
|
+
get cacheDir() {
|
|
84
|
+
return path.join(require('os').homedir(), '.prompd', 'cache');
|
|
85
|
+
}
|
|
86
|
+
get maxPackageSize() {
|
|
87
|
+
return 50 * 1024 * 1024; // 50MB
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Load config synchronously - required for constructor
|
|
91
|
+
*/
|
|
92
|
+
loadConfigSync(configManager) {
|
|
93
|
+
// If config is already loaded, use it
|
|
94
|
+
if (configManager.config) {
|
|
95
|
+
return configManager.config;
|
|
96
|
+
}
|
|
97
|
+
// Otherwise load it synchronously (this is a hack but necessary)
|
|
98
|
+
const fs = require('fs');
|
|
99
|
+
const yaml = require('yaml');
|
|
100
|
+
const os = require('os');
|
|
101
|
+
const path = require('path');
|
|
102
|
+
const configPath = path.join(os.homedir(), '.prompd', 'config.yaml');
|
|
103
|
+
// Use env var for registry URL if set (useful for local development)
|
|
104
|
+
const registryUrl = process.env.PROMPD_REGISTRY_URL || 'https://registry.prompdhub.ai';
|
|
105
|
+
const defaultConfig = {
|
|
106
|
+
apiKeys: {},
|
|
107
|
+
customProviders: {},
|
|
108
|
+
registry: {
|
|
109
|
+
registries: {
|
|
110
|
+
prompdhub: {
|
|
111
|
+
url: registryUrl
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
default: 'prompdhub'
|
|
115
|
+
},
|
|
116
|
+
scopes: {},
|
|
117
|
+
maxRetries: 3,
|
|
118
|
+
timeout: 30,
|
|
119
|
+
verbose: false
|
|
120
|
+
};
|
|
121
|
+
try {
|
|
122
|
+
if (fs.existsSync(configPath)) {
|
|
123
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
124
|
+
const fileConfig = yaml.parse(configContent);
|
|
125
|
+
// Merge with default config
|
|
126
|
+
const mergedConfig = { ...defaultConfig, ...fileConfig };
|
|
127
|
+
// Ensure registry structure exists
|
|
128
|
+
if (!mergedConfig.registry) {
|
|
129
|
+
mergedConfig.registry = defaultConfig.registry;
|
|
130
|
+
}
|
|
131
|
+
if (!mergedConfig.registry.registries) {
|
|
132
|
+
mergedConfig.registry.registries = defaultConfig.registry.registries;
|
|
133
|
+
}
|
|
134
|
+
if (!mergedConfig.scopes) {
|
|
135
|
+
mergedConfig.scopes = {};
|
|
136
|
+
}
|
|
137
|
+
return mergedConfig;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.warn(`Warning: Error loading config: ${error}`);
|
|
142
|
+
}
|
|
143
|
+
return defaultConfig;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Authenticate with registry using token
|
|
147
|
+
*/
|
|
148
|
+
async loginWithToken(token) {
|
|
149
|
+
// Verify token by getting user profile
|
|
150
|
+
const response = await fetch(`${this.registryUrl}/v1/user/me`, {
|
|
151
|
+
headers: {
|
|
152
|
+
'Authorization': `Bearer ${token}`,
|
|
153
|
+
'Content-Type': 'application/json'
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
if (response.status === 401) {
|
|
157
|
+
throw new Error('Invalid token. Please check your API token.');
|
|
158
|
+
}
|
|
159
|
+
else if (response.status === 404) {
|
|
160
|
+
throw new Error(`User endpoint not found. Registry URL: ${this.registryUrl}`);
|
|
161
|
+
}
|
|
162
|
+
else if (response.status >= 500) {
|
|
163
|
+
throw new Error(`Registry server error (${response.status}). Please try again later.`);
|
|
164
|
+
}
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
const errorText = await response.text();
|
|
167
|
+
throw new Error(`Authentication failed: ${errorText}`);
|
|
168
|
+
}
|
|
169
|
+
const userData = await response.json();
|
|
170
|
+
if (!userData.username) {
|
|
171
|
+
throw new Error('Invalid response from registry: missing username');
|
|
172
|
+
}
|
|
173
|
+
// Update registry config
|
|
174
|
+
const configManager = config_1.ConfigManager.getInstance();
|
|
175
|
+
await configManager.setRegistryToken(this.registryName, token, userData.username);
|
|
176
|
+
return { username: userData.username };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Clear authentication credentials
|
|
180
|
+
*/
|
|
181
|
+
async logout() {
|
|
182
|
+
const configManager = config_1.ConfigManager.getInstance();
|
|
183
|
+
await configManager.setRegistryToken(this.registryName, '', '');
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Publish a package to the registry
|
|
187
|
+
*/
|
|
188
|
+
async publish(packagePath, options = {
|
|
189
|
+
access: 'public',
|
|
190
|
+
tag: 'latest',
|
|
191
|
+
dryRun: false,
|
|
192
|
+
force: false
|
|
193
|
+
}) {
|
|
194
|
+
this.emit('publishStart', { packagePath, options });
|
|
195
|
+
try {
|
|
196
|
+
// Resolve file system (same pattern as compile)
|
|
197
|
+
const fileSystem = options.fileSystem || new file_system_1.NodeFileSystem();
|
|
198
|
+
// Load and validate package metadata from manifest.json
|
|
199
|
+
const metadata = await this.loadManifestFromFS(packagePath, fileSystem);
|
|
200
|
+
// Security validation using file system abstraction
|
|
201
|
+
await this.validatePackageForPublishFS(packagePath, metadata, fileSystem);
|
|
202
|
+
// Check version conflicts
|
|
203
|
+
if (!options.force) {
|
|
204
|
+
const existingVersions = await this.getPackageVersions(metadata.name);
|
|
205
|
+
if (existingVersions.includes(metadata.version)) {
|
|
206
|
+
throw new Error(`Version ${metadata.version} already exists. Use --force to overwrite.`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (options.dryRun) {
|
|
210
|
+
console.log('Dry run - package would be published:');
|
|
211
|
+
console.log(JSON.stringify(metadata, null, 2));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// Create package tarball Buffer (memory or disk)
|
|
215
|
+
const tarballBuffer = await this.createPackageTarballBuffer(packagePath, metadata, fileSystem);
|
|
216
|
+
// Upload to registry
|
|
217
|
+
await this.uploadPackageBuffer(tarballBuffer, metadata, options);
|
|
218
|
+
this.emit('publishComplete', {
|
|
219
|
+
name: metadata.name,
|
|
220
|
+
version: metadata.version,
|
|
221
|
+
access: options.access
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
this.emit('publishError', { packagePath, error });
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Install a package from the registry
|
|
231
|
+
* Accepts both formats:
|
|
232
|
+
* - @namespace/package (with options.version)
|
|
233
|
+
* - @namespace/package@version (embedded version)
|
|
234
|
+
*/
|
|
235
|
+
async install(packageName, options = {}) {
|
|
236
|
+
this.emit('installStart', { packageName, options });
|
|
237
|
+
console.log('[RegistryClient.install] Starting install:', packageName);
|
|
238
|
+
console.log('[RegistryClient.install] Options:', JSON.stringify(options));
|
|
239
|
+
try {
|
|
240
|
+
// Parse package reference if it includes @version
|
|
241
|
+
// Format: @namespace/package@version
|
|
242
|
+
// Find last @ to split name from version (scoped packages start with @)
|
|
243
|
+
let name = packageName;
|
|
244
|
+
let versionSpec = options.version || 'latest';
|
|
245
|
+
const lastAtIndex = packageName.lastIndexOf('@');
|
|
246
|
+
if (lastAtIndex > 0) {
|
|
247
|
+
// Has embedded version: @namespace/package@0.1.5
|
|
248
|
+
name = packageName.substring(0, lastAtIndex);
|
|
249
|
+
versionSpec = packageName.substring(lastAtIndex + 1);
|
|
250
|
+
}
|
|
251
|
+
console.log('[RegistryClient.install] Parsed name:', name, 'version:', versionSpec);
|
|
252
|
+
// Resolve version
|
|
253
|
+
const resolvedVersion = await this.resolveVersion(name, versionSpec);
|
|
254
|
+
console.log('[RegistryClient.install] Resolved version:', resolvedVersion);
|
|
255
|
+
// Check cache first
|
|
256
|
+
const cacheKey = `${name}@${resolvedVersion}`;
|
|
257
|
+
if (!options.skipCache && !options.force) {
|
|
258
|
+
const cachedPath = await this.getCachedPackage(cacheKey);
|
|
259
|
+
if (cachedPath) {
|
|
260
|
+
await this.installFromCache(cachedPath, name, resolvedVersion, options);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Download package
|
|
265
|
+
const packageData = await this.downloadPackage(name, resolvedVersion);
|
|
266
|
+
// Install dependencies (skip self-referential dependencies)
|
|
267
|
+
const metadata = packageData.metadata;
|
|
268
|
+
if (metadata.dependencies && Object.keys(metadata.dependencies).length > 0) {
|
|
269
|
+
// Filter out self-referential dependencies to avoid circular resolution
|
|
270
|
+
const filteredDeps = {};
|
|
271
|
+
for (const [depName, depVersion] of Object.entries(metadata.dependencies)) {
|
|
272
|
+
if (depName === name) {
|
|
273
|
+
// Skip self-referential dependency (package depends on itself)
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
filteredDeps[depName] = depVersion;
|
|
277
|
+
}
|
|
278
|
+
if (Object.keys(filteredDeps).length > 0) {
|
|
279
|
+
await this.installDependencies(filteredDeps, options);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Extract and install package
|
|
283
|
+
console.log('[RegistryClient.install] Extracting package to workspace...');
|
|
284
|
+
await this.extractAndInstallPackage(packageData, name, resolvedVersion, options);
|
|
285
|
+
console.log('[RegistryClient.install] Extraction complete');
|
|
286
|
+
// Cache package
|
|
287
|
+
await this.cachePackage(cacheKey, packageData);
|
|
288
|
+
this.emit('installComplete', {
|
|
289
|
+
name: name,
|
|
290
|
+
version: resolvedVersion
|
|
291
|
+
});
|
|
292
|
+
console.log('[RegistryClient.install] Install complete for', name, '@', resolvedVersion);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
this.emit('installError', { packageName, error });
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Search packages in the registry
|
|
301
|
+
*/
|
|
302
|
+
async search(query) {
|
|
303
|
+
try {
|
|
304
|
+
const searchParams = new URLSearchParams();
|
|
305
|
+
if (query.query)
|
|
306
|
+
searchParams.set('q', query.query);
|
|
307
|
+
if (query.category)
|
|
308
|
+
searchParams.set('category', query.category);
|
|
309
|
+
if (query.type)
|
|
310
|
+
searchParams.set('type', query.type);
|
|
311
|
+
if (query.tags)
|
|
312
|
+
searchParams.set('tags', query.tags.join(','));
|
|
313
|
+
if (query.author)
|
|
314
|
+
searchParams.set('author', query.author);
|
|
315
|
+
if (query.limit)
|
|
316
|
+
searchParams.set('limit', query.limit.toString());
|
|
317
|
+
if (query.offset)
|
|
318
|
+
searchParams.set('offset', query.offset.toString());
|
|
319
|
+
if (query.sort)
|
|
320
|
+
searchParams.set('sort', query.sort);
|
|
321
|
+
// Registry uses /packages?search= endpoint, not /search?q=
|
|
322
|
+
const response = await fetch(`${this.registryUrl}/packages?search=${encodeURIComponent(query.query || '')}`, {
|
|
323
|
+
headers: this.getAuthHeaders()
|
|
324
|
+
});
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
throw new Error(`Search failed: ${response.status} ${response.statusText}`);
|
|
327
|
+
}
|
|
328
|
+
const result = await response.json();
|
|
329
|
+
this.emit('searchComplete', { query, results: result.packages.length });
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
this.emit('searchError', { query, error });
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get package information
|
|
339
|
+
*/
|
|
340
|
+
async getPackageInfo(packageName, version) {
|
|
341
|
+
try {
|
|
342
|
+
const url = version
|
|
343
|
+
? `${this.registryUrl}/packages/${packageName}/${version}`
|
|
344
|
+
: `${this.registryUrl}/packages/${packageName}`;
|
|
345
|
+
const response = await fetch(url, {
|
|
346
|
+
headers: this.getAuthHeaders()
|
|
347
|
+
});
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
if (response.status === 404) {
|
|
350
|
+
throw new Error(`Package not found: ${packageName}`);
|
|
351
|
+
}
|
|
352
|
+
throw new Error(`Failed to get package info: ${response.status} ${response.statusText}`);
|
|
353
|
+
}
|
|
354
|
+
return await response.json();
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
this.emit('packageInfoError', { packageName, version, error });
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get available versions for a package
|
|
363
|
+
*/
|
|
364
|
+
async getPackageVersions(packageName) {
|
|
365
|
+
try {
|
|
366
|
+
const response = await fetch(`${this.registryUrl}/packages/${packageName}/versions`, {
|
|
367
|
+
headers: this.getAuthHeaders()
|
|
368
|
+
});
|
|
369
|
+
if (!response.ok) {
|
|
370
|
+
if (response.status === 404) {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
throw new Error(`Failed to get package versions: ${response.status} ${response.statusText}`);
|
|
374
|
+
}
|
|
375
|
+
const data = await response.json();
|
|
376
|
+
// Handle both response formats (like Python CLI does):
|
|
377
|
+
// 1. Direct array: [{version: "1.0.0", ...}, ...]
|
|
378
|
+
// 2. Wrapped object: {versions: [...]}
|
|
379
|
+
let versionsList;
|
|
380
|
+
if (Array.isArray(data)) {
|
|
381
|
+
versionsList = data;
|
|
382
|
+
}
|
|
383
|
+
else if (data.versions && Array.isArray(data.versions)) {
|
|
384
|
+
versionsList = data.versions;
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
throw new Error('Invalid versions response format');
|
|
388
|
+
}
|
|
389
|
+
// Extract version strings from version objects
|
|
390
|
+
return versionsList.map(v => typeof v === 'string' ? v : v.version);
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
this.emit('packageVersionsError', { packageName, error });
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async loadPackageMetadata(packageDir) {
|
|
398
|
+
const projectFile = path.join(packageDir, 'project.prmdproj');
|
|
399
|
+
if (!await fs.pathExists(projectFile)) {
|
|
400
|
+
throw new Error('No project.prmdproj file found');
|
|
401
|
+
}
|
|
402
|
+
const content = await fs.readFile(projectFile, 'utf8');
|
|
403
|
+
const yaml = require('yaml');
|
|
404
|
+
// Parse YAML frontmatter
|
|
405
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
406
|
+
if (!match) {
|
|
407
|
+
throw new Error('Invalid project.prmdproj file format');
|
|
408
|
+
}
|
|
409
|
+
const metadata = yaml.parse(match[1]);
|
|
410
|
+
// Validate required fields
|
|
411
|
+
const required = ['name', 'version', 'description', 'author'];
|
|
412
|
+
for (const field of required) {
|
|
413
|
+
if (!metadata[field]) {
|
|
414
|
+
throw new Error(`Missing required field: ${field}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Validate version format
|
|
418
|
+
if (!semver.valid(metadata.version)) {
|
|
419
|
+
throw new Error(`Invalid version format: ${metadata.version}`);
|
|
420
|
+
}
|
|
421
|
+
// Set defaults
|
|
422
|
+
metadata.license = metadata.license || 'MIT';
|
|
423
|
+
metadata.keywords = metadata.keywords || [];
|
|
424
|
+
metadata.dependencies = metadata.dependencies || {};
|
|
425
|
+
metadata.files = metadata.files || ['**/*'];
|
|
426
|
+
metadata.type = metadata.type || 'collection';
|
|
427
|
+
metadata.category = metadata.category || 'general';
|
|
428
|
+
metadata.tags = metadata.tags || [];
|
|
429
|
+
metadata.prmdVersion = '0.2.3';
|
|
430
|
+
metadata.createdAt = new Date().toISOString();
|
|
431
|
+
metadata.updatedAt = new Date().toISOString();
|
|
432
|
+
return metadata;
|
|
433
|
+
}
|
|
434
|
+
async validatePackageForPublish(packageDir, metadata) {
|
|
435
|
+
// Check package size
|
|
436
|
+
const stats = await this.getDirectorySize(packageDir);
|
|
437
|
+
if (stats.size > this.maxPackageSize) {
|
|
438
|
+
throw new Error(`Package size (${stats.size}) exceeds maximum allowed (${this.maxPackageSize})`);
|
|
439
|
+
}
|
|
440
|
+
// Validate package name
|
|
441
|
+
const sanitizedName = security_1.SecurityManager.sanitizeToolName(metadata.name);
|
|
442
|
+
if (sanitizedName !== metadata.name) {
|
|
443
|
+
throw new Error(`Invalid package name: ${metadata.name}`);
|
|
444
|
+
}
|
|
445
|
+
// Check for required files
|
|
446
|
+
const requiredFiles = [];
|
|
447
|
+
if (metadata.type === 'prompt' && metadata.main) {
|
|
448
|
+
requiredFiles.push(metadata.main);
|
|
449
|
+
}
|
|
450
|
+
for (const file of requiredFiles) {
|
|
451
|
+
const filePath = path.join(packageDir, file);
|
|
452
|
+
if (!await fs.pathExists(filePath)) {
|
|
453
|
+
throw new Error(`Required file not found: ${file}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// Validate dependencies
|
|
457
|
+
for (const [depName, depVersion] of Object.entries(metadata.dependencies)) {
|
|
458
|
+
if (!semver.validRange(depVersion)) {
|
|
459
|
+
throw new Error(`Invalid dependency version range: ${depName}@${depVersion}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async createPackageTarball(packageDir, metadata) {
|
|
464
|
+
const tarballName = `${metadata.name}-${metadata.version}.tgz`;
|
|
465
|
+
const tarballPath = path.join(this.cacheDir, 'temp', tarballName);
|
|
466
|
+
await fs.ensureDir(path.dirname(tarballPath));
|
|
467
|
+
// Create tarball with only specified files
|
|
468
|
+
await tar.create({
|
|
469
|
+
gzip: true,
|
|
470
|
+
file: tarballPath,
|
|
471
|
+
cwd: packageDir,
|
|
472
|
+
prefix: 'package/',
|
|
473
|
+
filter: (path) => {
|
|
474
|
+
// Include files matching patterns in metadata.files
|
|
475
|
+
return this.matchesFilePatterns(path, metadata.files);
|
|
476
|
+
}
|
|
477
|
+
}, ['.']);
|
|
478
|
+
return tarballPath;
|
|
479
|
+
}
|
|
480
|
+
async uploadPackage(tarballPath, metadata, options) {
|
|
481
|
+
const formData = new FormData();
|
|
482
|
+
const tarballBuffer = await fs.readFile(tarballPath);
|
|
483
|
+
formData.append('package', new Blob([tarballBuffer]), `${metadata.name}-${metadata.version}.tgz`);
|
|
484
|
+
formData.append('metadata', JSON.stringify(metadata));
|
|
485
|
+
formData.append('access', options.access);
|
|
486
|
+
formData.append('tag', options.tag);
|
|
487
|
+
const response = await fetch(`${this.registryUrl}/publish`, {
|
|
488
|
+
method: 'POST',
|
|
489
|
+
headers: this.getAuthHeaders(),
|
|
490
|
+
body: formData
|
|
491
|
+
});
|
|
492
|
+
if (!response.ok) {
|
|
493
|
+
const errorText = await response.text();
|
|
494
|
+
throw new Error(`Publish failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async resolveVersion(packageName, versionSpec) {
|
|
498
|
+
if (versionSpec === 'latest') {
|
|
499
|
+
const packageInfo = await this.getPackageInfo(packageName);
|
|
500
|
+
return packageInfo.version;
|
|
501
|
+
}
|
|
502
|
+
if (semver.valid(versionSpec)) {
|
|
503
|
+
return versionSpec;
|
|
504
|
+
}
|
|
505
|
+
// Resolve version range
|
|
506
|
+
const versions = await this.getPackageVersions(packageName);
|
|
507
|
+
const resolved = semver.maxSatisfying(versions, versionSpec);
|
|
508
|
+
if (!resolved) {
|
|
509
|
+
throw new Error(`No version found matching: ${versionSpec}`);
|
|
510
|
+
}
|
|
511
|
+
return resolved;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Download a package without installing it to disk.
|
|
515
|
+
* Useful for in-memory package loading.
|
|
516
|
+
*
|
|
517
|
+
* @param packageName - Package name (e.g., "@namespace/package-name")
|
|
518
|
+
* @param version - Package version (e.g., "1.0.0")
|
|
519
|
+
* @returns Object with tarball Buffer and package metadata
|
|
520
|
+
*/
|
|
521
|
+
async downloadPackageBuffer(packageName, version) {
|
|
522
|
+
return this.downloadPackage(packageName, version);
|
|
523
|
+
}
|
|
524
|
+
async downloadPackage(packageName, version) {
|
|
525
|
+
// Registry endpoint format: /packages/@scope/name/download/version
|
|
526
|
+
const response = await fetch(`${this.registryUrl}/packages/${packageName}/download/${version}`, {
|
|
527
|
+
headers: this.getAuthHeaders()
|
|
528
|
+
});
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
531
|
+
}
|
|
532
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
533
|
+
const tarballBuffer = Buffer.from(arrayBuffer);
|
|
534
|
+
// Extract metadata from the .pdpkg (ZIP) file instead of calling getPackageInfo
|
|
535
|
+
// Try prompd.json first, fall back to manifest.json for older packages
|
|
536
|
+
const AdmZip = (await Promise.resolve().then(() => __importStar(require('adm-zip')))).default;
|
|
537
|
+
const zip = new AdmZip(tarballBuffer);
|
|
538
|
+
// Check for prompd.json first (newer format), then manifest.json (legacy)
|
|
539
|
+
let manifestEntry = zip.getEntry('prompd.json');
|
|
540
|
+
if (!manifestEntry) {
|
|
541
|
+
manifestEntry = zip.getEntry('manifest.json');
|
|
542
|
+
}
|
|
543
|
+
if (!manifestEntry) {
|
|
544
|
+
throw new Error(`Invalid package: neither prompd.json nor manifest.json found in ${packageName}@${version}`);
|
|
545
|
+
}
|
|
546
|
+
const manifestContent = manifestEntry.getData().toString('utf8');
|
|
547
|
+
const metadata = JSON.parse(manifestContent);
|
|
548
|
+
return {
|
|
549
|
+
tarball: tarballBuffer,
|
|
550
|
+
metadata
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
async installDependencies(dependencies, options) {
|
|
554
|
+
for (const [name, versionSpec] of Object.entries(dependencies)) {
|
|
555
|
+
this.emit('installingDependency', { name, version: versionSpec });
|
|
556
|
+
await this.install(name, { ...options, version: versionSpec });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async extractAndInstallPackage(packageData, packageName, version, options) {
|
|
560
|
+
// Use workspace root from options if provided, otherwise use cwd
|
|
561
|
+
const workspaceRoot = options.workspaceRoot || process.cwd();
|
|
562
|
+
console.log('[RegistryClient.extractAndInstallPackage] packageName:', packageName);
|
|
563
|
+
console.log('[RegistryClient.extractAndInstallPackage] version:', version);
|
|
564
|
+
console.log('[RegistryClient.extractAndInstallPackage] workspaceRoot:', workspaceRoot);
|
|
565
|
+
console.log('[RegistryClient.extractAndInstallPackage] global:', options.global);
|
|
566
|
+
const installDir = options.global
|
|
567
|
+
? path.join(this.cacheDir, 'global', 'packages', packageName, version)
|
|
568
|
+
: path.join(workspaceRoot, '.prompd', 'cache', packageName, version);
|
|
569
|
+
console.log('[RegistryClient.extractAndInstallPackage] installDir:', installDir);
|
|
570
|
+
await fs.ensureDir(installDir);
|
|
571
|
+
console.log('[RegistryClient.extractAndInstallPackage] Directory ensured');
|
|
572
|
+
// Extract .pdpkg (ZIP archive) using adm-zip
|
|
573
|
+
const AdmZip = (await Promise.resolve().then(() => __importStar(require('adm-zip')))).default;
|
|
574
|
+
const zip = new AdmZip(packageData.tarball);
|
|
575
|
+
// Log zip contents
|
|
576
|
+
const zipEntries = zip.getEntries();
|
|
577
|
+
console.log('[RegistryClient.extractAndInstallPackage] ZIP contains', zipEntries.length, 'entries:');
|
|
578
|
+
zipEntries.forEach(entry => {
|
|
579
|
+
console.log(' -', entry.entryName);
|
|
580
|
+
});
|
|
581
|
+
zip.extractAllTo(installDir, true);
|
|
582
|
+
console.log('[RegistryClient.extractAndInstallPackage] ZIP extracted to:', installDir);
|
|
583
|
+
// Verify extraction
|
|
584
|
+
const extractedFiles = await fs.readdir(installDir);
|
|
585
|
+
console.log('[RegistryClient.extractAndInstallPackage] Extracted files:', extractedFiles);
|
|
586
|
+
// Write package metadata for cache tracking
|
|
587
|
+
const metadataPath = path.join(installDir, '.prmdmeta');
|
|
588
|
+
await fs.writeJson(metadataPath, packageData.metadata, { spaces: 2 });
|
|
589
|
+
console.log('[RegistryClient.extractAndInstallPackage] Wrote .prmdmeta');
|
|
590
|
+
}
|
|
591
|
+
getAuthHeaders() {
|
|
592
|
+
const headers = {
|
|
593
|
+
'User-Agent': 'prompd-cli/0.2.4',
|
|
594
|
+
'Content-Type': 'application/json'
|
|
595
|
+
};
|
|
596
|
+
if (this.authToken) {
|
|
597
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
598
|
+
}
|
|
599
|
+
return headers;
|
|
600
|
+
}
|
|
601
|
+
async ensureCacheDir() {
|
|
602
|
+
await fs.ensureDir(this.cacheDir);
|
|
603
|
+
await fs.ensureDir(path.join(this.cacheDir, 'temp'));
|
|
604
|
+
}
|
|
605
|
+
async getDirectorySize(dir) {
|
|
606
|
+
let totalSize = 0;
|
|
607
|
+
let totalFiles = 0;
|
|
608
|
+
const items = await fs.readdir(dir);
|
|
609
|
+
for (const item of items) {
|
|
610
|
+
const fullPath = path.join(dir, item);
|
|
611
|
+
const stats = await fs.stat(fullPath);
|
|
612
|
+
if (stats.isDirectory()) {
|
|
613
|
+
const subStats = await this.getDirectorySize(fullPath);
|
|
614
|
+
totalSize += subStats.size;
|
|
615
|
+
totalFiles += subStats.files;
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
totalSize += stats.size;
|
|
619
|
+
totalFiles++;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return { size: totalSize, files: totalFiles };
|
|
623
|
+
}
|
|
624
|
+
matchesFilePatterns(filePath, patterns) {
|
|
625
|
+
// Simple glob matching - in production would use proper glob library
|
|
626
|
+
for (const pattern of patterns) {
|
|
627
|
+
if (pattern === '**/*' || pattern === '*') {
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
if (pattern.includes('*')) {
|
|
631
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
632
|
+
if (regex.test(filePath)) {
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
else if (filePath === pattern) {
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
async getCachedPackage(cacheKey) {
|
|
643
|
+
const cachePath = path.join(this.cacheDir, 'packages', cacheKey);
|
|
644
|
+
return await fs.pathExists(cachePath) ? cachePath : null;
|
|
645
|
+
}
|
|
646
|
+
async installFromCache(cachePath, packageName, version, options) {
|
|
647
|
+
console.log('[RegistryClient.installFromCache] Installing from cache:', cachePath);
|
|
648
|
+
this.emit('installingFromCache', { name: packageName, version });
|
|
649
|
+
// Read the cached tarball
|
|
650
|
+
const tarballBuffer = await fs.readFile(cachePath);
|
|
651
|
+
console.log('[RegistryClient.installFromCache] Read tarball:', tarballBuffer.length, 'bytes');
|
|
652
|
+
// Extract to the workspace
|
|
653
|
+
const packageData = { tarball: tarballBuffer, metadata: { name: packageName, version } };
|
|
654
|
+
await this.extractAndInstallPackage(packageData, packageName, version, options);
|
|
655
|
+
console.log('[RegistryClient.installFromCache] Extraction complete');
|
|
656
|
+
}
|
|
657
|
+
async cachePackage(cacheKey, packageData) {
|
|
658
|
+
const cachePath = path.join(this.cacheDir, 'packages', cacheKey);
|
|
659
|
+
await fs.ensureDir(path.dirname(cachePath));
|
|
660
|
+
await fs.writeFile(cachePath, packageData.tarball);
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Load manifest.json from file system abstraction.
|
|
664
|
+
* Supports both disk-based and in-memory file systems.
|
|
665
|
+
*/
|
|
666
|
+
async loadManifestFromFS(packagePath, fileSystem) {
|
|
667
|
+
const manifestPath = fileSystem.join(packagePath, 'manifest.json');
|
|
668
|
+
const exists = await Promise.resolve(fileSystem.exists(manifestPath));
|
|
669
|
+
if (!exists) {
|
|
670
|
+
throw new Error('No manifest.json file found');
|
|
671
|
+
}
|
|
672
|
+
const content = await Promise.resolve(fileSystem.readFile(manifestPath));
|
|
673
|
+
const manifest = JSON.parse(content);
|
|
674
|
+
// Validate required fields
|
|
675
|
+
const required = ['name', 'version', 'description', 'author'];
|
|
676
|
+
for (const field of required) {
|
|
677
|
+
if (!manifest[field]) {
|
|
678
|
+
throw new Error(`Missing required field in manifest.json: ${field}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// Validate version format
|
|
682
|
+
if (!semver.valid(manifest.version)) {
|
|
683
|
+
throw new Error(`Invalid version format: ${manifest.version}`);
|
|
684
|
+
}
|
|
685
|
+
// Set defaults
|
|
686
|
+
manifest.license = manifest.license || 'MIT';
|
|
687
|
+
manifest.keywords = manifest.keywords || [];
|
|
688
|
+
manifest.dependencies = manifest.dependencies || {};
|
|
689
|
+
manifest.files = manifest.files || ['**/*.prmd'];
|
|
690
|
+
manifest.type = manifest.type || 'collection';
|
|
691
|
+
manifest.category = manifest.category || 'general';
|
|
692
|
+
manifest.tags = manifest.tags || [];
|
|
693
|
+
manifest.prompdVersion = manifest.prompdVersion || '0.3.3';
|
|
694
|
+
manifest.createdAt = manifest.createdAt || new Date().toISOString();
|
|
695
|
+
manifest.updatedAt = new Date().toISOString();
|
|
696
|
+
return manifest;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Validate package for publish using file system abstraction.
|
|
700
|
+
*/
|
|
701
|
+
async validatePackageForPublishFS(packagePath, metadata, fileSystem) {
|
|
702
|
+
// Calculate package size
|
|
703
|
+
const stats = fileSystem instanceof file_system_1.MemoryFileSystem
|
|
704
|
+
? fileSystem.getTotalSize(packagePath)
|
|
705
|
+
: await this.getDirectorySize(packagePath);
|
|
706
|
+
if (stats.size > this.maxPackageSize) {
|
|
707
|
+
throw new Error(`Package size (${stats.size}) exceeds maximum allowed (${this.maxPackageSize})`);
|
|
708
|
+
}
|
|
709
|
+
// Validate package name (use existing security manager)
|
|
710
|
+
const sanitizedName = security_1.SecurityManager.sanitizeToolName(metadata.name);
|
|
711
|
+
if (sanitizedName !== metadata.name) {
|
|
712
|
+
throw new Error(`Invalid package name: ${metadata.name}`);
|
|
713
|
+
}
|
|
714
|
+
// Validate dependencies
|
|
715
|
+
for (const [depName, depVersion] of Object.entries(metadata.dependencies)) {
|
|
716
|
+
if (!semver.validRange(depVersion)) {
|
|
717
|
+
throw new Error(`Invalid dependency version range: ${depName}@${depVersion}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Create package tarball Buffer using file system abstraction.
|
|
723
|
+
* Supports both in-memory and disk-based package creation.
|
|
724
|
+
*/
|
|
725
|
+
async createPackageTarballBuffer(packagePath, metadata, fileSystem) {
|
|
726
|
+
// Memory-based path
|
|
727
|
+
if (fileSystem instanceof file_system_1.MemoryFileSystem) {
|
|
728
|
+
return await fileSystem.createPackageBuffer(packagePath, metadata, {
|
|
729
|
+
filter: (filePath) => this.matchesFilePatterns(filePath, metadata.files)
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
// Disk-based path (existing logic)
|
|
733
|
+
const tarballName = `${metadata.name}-${metadata.version}.tgz`;
|
|
734
|
+
const tarballPath = path.join(this.cacheDir, 'temp', tarballName);
|
|
735
|
+
await fs.ensureDir(path.dirname(tarballPath));
|
|
736
|
+
await tar.create({
|
|
737
|
+
gzip: true,
|
|
738
|
+
file: tarballPath,
|
|
739
|
+
cwd: packagePath,
|
|
740
|
+
prefix: 'package/',
|
|
741
|
+
filter: (filePath) => this.matchesFilePatterns(filePath, metadata.files)
|
|
742
|
+
}, ['.']);
|
|
743
|
+
const buffer = await fs.readFile(tarballPath);
|
|
744
|
+
await fs.remove(tarballPath); // Cleanup
|
|
745
|
+
return buffer;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Upload package Buffer to registry.
|
|
749
|
+
*/
|
|
750
|
+
async uploadPackageBuffer(tarballBuffer, metadata, options) {
|
|
751
|
+
const FormData = require('form-data');
|
|
752
|
+
const formData = new FormData();
|
|
753
|
+
formData.append('package', tarballBuffer, {
|
|
754
|
+
filename: `${metadata.name}-${metadata.version}.pdpkg`,
|
|
755
|
+
contentType: 'application/gzip'
|
|
756
|
+
});
|
|
757
|
+
formData.append('metadata', JSON.stringify(metadata));
|
|
758
|
+
formData.append('access', options.access);
|
|
759
|
+
formData.append('tag', options.tag);
|
|
760
|
+
const response = await fetch(`${this.registryUrl}/publish`, {
|
|
761
|
+
method: 'POST',
|
|
762
|
+
headers: {
|
|
763
|
+
...this.getAuthHeaders(),
|
|
764
|
+
...formData.getHeaders()
|
|
765
|
+
},
|
|
766
|
+
body: formData
|
|
767
|
+
});
|
|
768
|
+
if (!response.ok) {
|
|
769
|
+
const errorText = await response.text();
|
|
770
|
+
throw new Error(`Publish failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
exports.RegistryClient = RegistryClient;
|
|
775
|
+
/**
|
|
776
|
+
* Default registry configuration
|
|
777
|
+
*/
|
|
778
|
+
const createDefaultRegistryConfig = () => ({
|
|
779
|
+
registryUrl: process.env.PROMPD_REGISTRY_URL || 'https://registry.prompdhub.ai',
|
|
780
|
+
authToken: process.env.PROMPD_AUTH_TOKEN,
|
|
781
|
+
cacheDir: path.join(require('os').homedir(), '.prmd', 'cache'),
|
|
782
|
+
timeout: 30000,
|
|
783
|
+
maxPackageSize: 50 * 1024 * 1024 // 50MB
|
|
784
|
+
});
|
|
785
|
+
exports.createDefaultRegistryConfig = createDefaultRegistryConfig;
|
|
786
|
+
//# sourceMappingURL=registry.js.map
|