repofence 0.2.0 → 0.2.2
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/cli.js +3 -3
- package/dist/commands/auth.js +25 -5
- package/dist/commands/init.js +3 -3
- package/dist/commands/status.js +6 -6
- package/dist/core/api-client.js +12 -3
- package/dist/core/auth.js +1 -1
- package/dist/core/pack-manager.js +17 -17
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -119,9 +119,9 @@ program
|
|
|
119
119
|
// Auth command
|
|
120
120
|
program
|
|
121
121
|
.command('auth')
|
|
122
|
-
.description('
|
|
123
|
-
.option('--token <token>', 'Token
|
|
124
|
-
.option('--web', '
|
|
122
|
+
.description('Authenticate the CLI with a token from repofence.com')
|
|
123
|
+
.option('--token <token>', 'Token from repofence.com (after purchase)')
|
|
124
|
+
.option('--web', 'Open login/checkout in the browser to get a token')
|
|
125
125
|
.action(async (options) => {
|
|
126
126
|
const cwd = process.cwd();
|
|
127
127
|
try {
|
package/dist/commands/auth.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const promises_1 = __importDefault(require("readline/promises"));
|
|
9
9
|
const process_1 = require("process");
|
|
10
10
|
const auth_1 = require("../core/auth");
|
|
11
|
+
const api_client_1 = require("../core/api-client");
|
|
11
12
|
const open_1 = __importDefault(require("open"));
|
|
12
13
|
const promptToken = async () => {
|
|
13
14
|
const rl = promises_1.default.createInterface({ input: process_1.stdin, output: process_1.stdout });
|
|
@@ -15,6 +16,7 @@ const promptToken = async () => {
|
|
|
15
16
|
rl.close();
|
|
16
17
|
return answer;
|
|
17
18
|
};
|
|
19
|
+
const isJwt = (token) => token.split('.').length === 3;
|
|
18
20
|
const authCommand = async (_cwd, options) => {
|
|
19
21
|
if (options.web) {
|
|
20
22
|
const authBase = process.env.REPOFENCE_AUTH_URL || 'https://repofence.com/pricing';
|
|
@@ -34,12 +36,30 @@ const authCommand = async (_cwd, options) => {
|
|
|
34
36
|
console.log(chalk_1.default.dim('─'.repeat(60)) + '\n');
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
39
|
-
throw new Error('No
|
|
39
|
+
const rawToken = options.token ?? (await promptToken());
|
|
40
|
+
if (!rawToken) {
|
|
41
|
+
throw new Error('No token provided; try again with --token <value>.');
|
|
42
|
+
}
|
|
43
|
+
// Raw license tokens (hex) need to be exchanged for a JWT via /activate.
|
|
44
|
+
// Already-valid JWTs (e.g. from refresh) are saved directly.
|
|
45
|
+
if (isJwt(rawToken)) {
|
|
46
|
+
await (0, auth_1.saveToken)(rawToken);
|
|
47
|
+
console.log(chalk_1.default.green(`✅ Token saved to ${auth_1.tokenPath}`));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write('Activating license...');
|
|
51
|
+
try {
|
|
52
|
+
const client = new api_client_1.ApiClient();
|
|
53
|
+
const { token: jwt } = await client.activateToken(rawToken);
|
|
54
|
+
await (0, auth_1.saveToken)(jwt);
|
|
55
|
+
process.stdout.write('\r');
|
|
56
|
+
console.log(chalk_1.default.green(`✅ License activated and token saved to ${auth_1.tokenPath}`));
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
process.stdout.write('\r');
|
|
60
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
+
throw new Error(`Failed to activate license: ${message}`);
|
|
40
62
|
}
|
|
41
|
-
await (0, auth_1.saveToken)(token);
|
|
42
|
-
console.log(chalk_1.default.green(`✅ Token guardado en ${auth_1.tokenPath}`));
|
|
43
63
|
};
|
|
44
64
|
exports.authCommand = authCommand;
|
|
45
65
|
//# sourceMappingURL=auth.js.map
|
package/dist/commands/init.js
CHANGED
|
@@ -185,17 +185,17 @@ async function initCommand(cwd, options) {
|
|
|
185
185
|
}
|
|
186
186
|
catch (error) {
|
|
187
187
|
const message = error instanceof Error ? error.message : String(error);
|
|
188
|
-
console.error(chalk_1.default.red(`❌
|
|
188
|
+
console.error(chalk_1.default.red(`❌ Failed to download pack "${packId}" (${resolvedBase}): ${message}`));
|
|
189
189
|
throw error;
|
|
190
190
|
}
|
|
191
191
|
const isValid = await (0, pack_manager_1.validateSignature)(pack);
|
|
192
192
|
if (!isValid) {
|
|
193
|
-
throw new Error('
|
|
193
|
+
throw new Error('Invalid pack signature.');
|
|
194
194
|
}
|
|
195
195
|
await (0, pack_manager_1.installPack)(pack, { force, verbose, target: ide }, profile.installBaseDir);
|
|
196
196
|
packInstalled = true;
|
|
197
197
|
if (verbose) {
|
|
198
|
-
console.log(chalk_1.default.green(`✓ Pack "${pack.packId}"
|
|
198
|
+
console.log(chalk_1.default.green(`✓ Pack "${pack.packId}" installed`));
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
else {
|
package/dist/commands/status.js
CHANGED
|
@@ -13,13 +13,13 @@ const printManifest = async (target) => {
|
|
|
13
13
|
const manifest = await (0, pack_manager_1.readManifest)(profile.installBaseDir);
|
|
14
14
|
console.log(chalk_1.default.bold(`\n[${profile.displayName}]`));
|
|
15
15
|
if (!manifest) {
|
|
16
|
-
console.log(chalk_1.default.yellow(`⚠️ No
|
|
16
|
+
console.log(chalk_1.default.yellow(`⚠️ No pack manifest found at ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
console.log(chalk_1.default.cyan(`Pack: ${manifest.pack_id} v${manifest.pack_version}`));
|
|
20
|
-
console.log(chalk_1.default.cyan(`
|
|
21
|
-
console.log(chalk_1.default.cyan(`
|
|
22
|
-
console.log(chalk_1.default.cyan(`
|
|
20
|
+
console.log(chalk_1.default.cyan(`Installed: ${manifest.installed_at}`));
|
|
21
|
+
console.log(chalk_1.default.cyan(`Expires: ${manifest.expires_at ?? 'never'}`));
|
|
22
|
+
console.log(chalk_1.default.cyan(`Artifacts:`));
|
|
23
23
|
manifest.commands.forEach((cmd) => console.log(` - ${cmd}`));
|
|
24
24
|
console.log(chalk_1.default.gray(`Manifest: ${(0, pack_manager_1.manifestFilePath)(profile.installBaseDir)}`));
|
|
25
25
|
};
|
|
@@ -28,10 +28,10 @@ const statusCommand = async (cwd, options = {}) => {
|
|
|
28
28
|
const target = options.ide || 'all';
|
|
29
29
|
const token = await (0, auth_1.readToken)();
|
|
30
30
|
if (token) {
|
|
31
|
-
console.log(chalk_1.default.green(`✅ Token
|
|
31
|
+
console.log(chalk_1.default.green(`✅ Token present (${auth_1.tokenPath})`));
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
|
-
console.log(chalk_1.default.yellow('⚠️
|
|
34
|
+
console.log(chalk_1.default.yellow('⚠️ No token configured. Run `repofence auth --token <value>`'));
|
|
35
35
|
}
|
|
36
36
|
if (target === 'all') {
|
|
37
37
|
await printManifest('cursor');
|
package/dist/core/api-client.js
CHANGED
|
@@ -21,16 +21,16 @@ const DEMO_PACK = {
|
|
|
21
21
|
files: [
|
|
22
22
|
{
|
|
23
23
|
path: 'repofence-spec.md',
|
|
24
|
-
contentBase64: Buffer.from('# repofence spec (demo)\n\n_TODO:
|
|
24
|
+
contentBase64: Buffer.from('# repofence spec (demo)\n\n_TODO: pack content_\n').toString('base64'),
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
path: 'repofence-plan.md',
|
|
28
|
-
contentBase64: Buffer.from('# repofence plan (demo)\n\n_TODO:
|
|
28
|
+
contentBase64: Buffer.from('# repofence plan (demo)\n\n_TODO: pack content_\n').toString('base64'),
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
};
|
|
32
32
|
const notImplemented = () => {
|
|
33
|
-
throw new Error('API client
|
|
33
|
+
throw new Error('API client not implemented. Set REPOFENCE_MOCK=1 for dummy data.');
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
36
|
* Production hosts expose routes under `/api/v1/...`.
|
|
@@ -86,6 +86,15 @@ class ApiClient {
|
|
|
86
86
|
}
|
|
87
87
|
return json;
|
|
88
88
|
}
|
|
89
|
+
async activateToken(rawToken) {
|
|
90
|
+
if (process.env.REPOFENCE_MOCK === '1') {
|
|
91
|
+
return { token: 'demo-token', license_id: 'lic-demo', packs: ['core'] };
|
|
92
|
+
}
|
|
93
|
+
return this.request('/v1/auth/activate', {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
body: JSON.stringify({ token: rawToken }),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
89
98
|
async loginWithEmail(email) {
|
|
90
99
|
if (process.env.REPOFENCE_MOCK === '1') {
|
|
91
100
|
return { token: 'demo-token', license_id: 'lic-demo', packs: ['core'] };
|
package/dist/core/auth.js
CHANGED
|
@@ -16,7 +16,7 @@ exports.tokenPath = tokenFile;
|
|
|
16
16
|
const saveToken = async (token) => {
|
|
17
17
|
const normalized = token.trim();
|
|
18
18
|
if (!normalized) {
|
|
19
|
-
throw new Error('
|
|
19
|
+
throw new Error('Empty token; provide a valid token.');
|
|
20
20
|
}
|
|
21
21
|
await ensureDir();
|
|
22
22
|
await promises_1.default.writeFile(tokenFile, normalized, { mode: 0o600 });
|
|
@@ -49,7 +49,7 @@ const sha256Base64 = (buf) => crypto_1.default.createHash('sha256').update(buf).
|
|
|
49
49
|
const downloadBuffer = async (url) => {
|
|
50
50
|
const res = await fetch(url);
|
|
51
51
|
if (!res.ok) {
|
|
52
|
-
throw new Error(`
|
|
52
|
+
throw new Error(`Download failed (${res.status}) ${res.statusText}`);
|
|
53
53
|
}
|
|
54
54
|
const arrayBuffer = await res.arrayBuffer();
|
|
55
55
|
return Buffer.from(arrayBuffer);
|
|
@@ -137,29 +137,29 @@ const installFromArchive = async (pack, options, baseDir) => {
|
|
|
137
137
|
const dir = commandsDir(baseDir);
|
|
138
138
|
await promises_1.default.mkdir(dir, { recursive: true });
|
|
139
139
|
const archiveUrl = pack.archiveUrl;
|
|
140
|
-
//
|
|
140
|
+
// Download manifest and recalculate hash (robustness)
|
|
141
141
|
let manifestHashCalculated = undefined;
|
|
142
142
|
if (pack.manifestUrl) {
|
|
143
143
|
const manifestBuf = await downloadBuffer(pack.manifestUrl);
|
|
144
144
|
manifestHashCalculated = sha256Base64(manifestBuf);
|
|
145
145
|
if (pack.manifestHash && pack.manifestHash !== manifestHashCalculated) {
|
|
146
|
-
throw new Error(`
|
|
146
|
+
throw new Error(`Invalid manifest hash. Expected ${pack.manifestHash}, got ${manifestHashCalculated}`);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
const archiveBuf = await downloadBuffer(archiveUrl);
|
|
150
150
|
if (pack.archiveHash) {
|
|
151
151
|
const hash = sha256Base64(archiveBuf);
|
|
152
152
|
if (hash !== pack.archiveHash) {
|
|
153
|
-
throw new Error(`
|
|
153
|
+
throw new Error(`Invalid archive hash. Expected ${pack.archiveHash}, got ${hash}`);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
//
|
|
156
|
+
// List files, then extract archive contents
|
|
157
157
|
const tmpDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'repofence-archive-'));
|
|
158
158
|
const archivePath = path_1.default.join(tmpDir, 'pack.tar.gz');
|
|
159
159
|
const extractedPath = path_1.default.join(tmpDir, 'extracted');
|
|
160
160
|
await promises_1.default.mkdir(extractedPath, { recursive: true });
|
|
161
161
|
await promises_1.default.writeFile(archivePath, archiveBuf);
|
|
162
|
-
//
|
|
162
|
+
// Signature validation (manifestHash from pack; archiveHash validated above)
|
|
163
163
|
await (0, exports.validateSignature)({
|
|
164
164
|
...pack,
|
|
165
165
|
manifestHash: pack.manifestHash || manifestHashCalculated,
|
|
@@ -173,7 +173,7 @@ const installFromArchive = async (pack, options, baseDir) => {
|
|
|
173
173
|
: await installCursorFromExtracted(extractedPath, pack, baseDir);
|
|
174
174
|
await promises_1.default.rm(tmpDir, { recursive: true, force: true });
|
|
175
175
|
if (options.verbose) {
|
|
176
|
-
console.log(`[repofence]
|
|
176
|
+
console.log(`[repofence] installed from archive ${archiveUrl}`);
|
|
177
177
|
}
|
|
178
178
|
return manifest;
|
|
179
179
|
};
|
|
@@ -209,7 +209,7 @@ const installClaudeFromExtracted = async (extractedPath, pack, baseDir, options)
|
|
|
209
209
|
const { skillName, content } = toClaudeSkillMarkdown(rel, sourceMarkdown);
|
|
210
210
|
const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
|
|
211
211
|
if (!options.force && (await fileExists(targetPath))) {
|
|
212
|
-
throw new Error(`
|
|
212
|
+
throw new Error(`Skill already exists: ${targetPath} (use --force to overwrite)`);
|
|
213
213
|
}
|
|
214
214
|
await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
|
|
215
215
|
await promises_1.default.writeFile(targetPath, content, 'utf8');
|
|
@@ -227,7 +227,7 @@ const installClaudeFromExtracted = async (extractedPath, pack, baseDir, options)
|
|
|
227
227
|
};
|
|
228
228
|
const installPack = async (pack, options = {}, baseDir) => {
|
|
229
229
|
const target = options.target || 'cursor';
|
|
230
|
-
//
|
|
230
|
+
// If archiveUrl is set, download tar.gz and extract.
|
|
231
231
|
if (pack.archiveUrl) {
|
|
232
232
|
return installFromArchive(pack, options, baseDir);
|
|
233
233
|
}
|
|
@@ -240,13 +240,13 @@ const installPack = async (pack, options = {}, baseDir) => {
|
|
|
240
240
|
const { skillName, content } = toClaudeSkillMarkdown(file.path, source);
|
|
241
241
|
const targetPath = path_1.default.join(dir, skillName, 'SKILL.md');
|
|
242
242
|
if (!options.force && (await fileExists(targetPath))) {
|
|
243
|
-
throw new Error(`
|
|
243
|
+
throw new Error(`Skill already exists: ${targetPath} (use --force to overwrite)`);
|
|
244
244
|
}
|
|
245
245
|
await promises_1.default.mkdir(path_1.default.dirname(targetPath), { recursive: true });
|
|
246
246
|
await promises_1.default.writeFile(targetPath, content, 'utf8');
|
|
247
247
|
artifacts.push(path_1.default.relative(dir, targetPath));
|
|
248
248
|
if (options.verbose) {
|
|
249
|
-
console.log(`[repofence] skill
|
|
249
|
+
console.log(`[repofence] wrote skill ${targetPath}`);
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
const manifest = {
|
|
@@ -262,13 +262,13 @@ const installPack = async (pack, options = {}, baseDir) => {
|
|
|
262
262
|
for (const file of pack.files) {
|
|
263
263
|
const target = path_1.default.join(dir, file.path);
|
|
264
264
|
if (!options.force && (await fileExists(target))) {
|
|
265
|
-
throw new Error(`
|
|
265
|
+
throw new Error(`File already exists: ${target} (use --force to overwrite)`);
|
|
266
266
|
}
|
|
267
267
|
await promises_1.default.mkdir(path_1.default.dirname(target), { recursive: true });
|
|
268
268
|
const buffer = Buffer.from(file.contentBase64, 'base64');
|
|
269
269
|
await promises_1.default.writeFile(target, buffer);
|
|
270
270
|
if (options.verbose) {
|
|
271
|
-
console.log(`[repofence]
|
|
271
|
+
console.log(`[repofence] wrote ${target}`);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
const manifest = {
|
|
@@ -286,11 +286,11 @@ const manifestFilePath = (baseDir) => manifestPath(baseDir);
|
|
|
286
286
|
exports.manifestFilePath = manifestFilePath;
|
|
287
287
|
const validateSignature = async (_pack) => {
|
|
288
288
|
const pack = _pack;
|
|
289
|
-
//
|
|
289
|
+
// If there is no signature or hashes, skip validation (demo/mock mode).
|
|
290
290
|
if (!pack.signature || !pack.manifestHash) {
|
|
291
291
|
return true;
|
|
292
292
|
}
|
|
293
|
-
//
|
|
293
|
+
// Build payload same as backend: manifestHash[:archiveHash]
|
|
294
294
|
const payloadToVerify = pack.archiveHash
|
|
295
295
|
? `${pack.manifestHash}:${pack.archiveHash}`
|
|
296
296
|
: pack.manifestHash;
|
|
@@ -302,12 +302,12 @@ const validateSignature = async (_pack) => {
|
|
|
302
302
|
const keyObject = crypto_1.default.createPublicKey(publicKeyPem);
|
|
303
303
|
const isValid = crypto_1.default.verify(null, Buffer.from(payloadToVerify), keyObject, Buffer.from(pack.signature, 'base64'));
|
|
304
304
|
if (!isValid) {
|
|
305
|
-
throw new Error('
|
|
305
|
+
throw new Error('Invalid pack signature.');
|
|
306
306
|
}
|
|
307
307
|
return true;
|
|
308
308
|
}
|
|
309
309
|
catch (error) {
|
|
310
|
-
const msg = error instanceof Error ? error.message : '
|
|
310
|
+
const msg = error instanceof Error ? error.message : 'signature_validation_error';
|
|
311
311
|
throw new Error(msg);
|
|
312
312
|
}
|
|
313
313
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repofence",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Repofence CLI (packs + backend auth)",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,10 +33,12 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@aws-sdk/client-s3": "^3.705.0",
|
|
35
35
|
"@aws-sdk/s3-request-presigner": "^3.705.0",
|
|
36
|
+
"@supabase/supabase-js": "^2.103.0",
|
|
36
37
|
"chalk": "^4.1.2",
|
|
37
38
|
"commander": "^11.1.0",
|
|
38
39
|
"dotenv": "^16.4.5",
|
|
39
40
|
"open": "^11.0.0",
|
|
41
|
+
"resend": "^6.10.0",
|
|
40
42
|
"tar": "^6.2.1"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|